Are python objects thread-safe?

Aaron Brady castironpi at gmail.com
Tue Dec 23 15:23:45 EST 2008


On Dec 23, 7:30 am, Duncan Booth <duncan.bo... at invalid.invalid> wrote:
> Aaron Brady <castiro... at gmail.com> wrote:
> > Th.1   Th.2
> > a=X
> >        a=Y
> > a=Z
>
> > You are saying that if 'a=Z' interrupts 'a=Y' at the wrong time, the
> > destructor for 'X' or 'Y' might not get called.  Correct?  In serial
> > flow, the destructor for X is called, then Y.
>
> No, the destructors will be called, but the destructors can do pretty much
> anything they want so you can't say the assignment is atomic. This isn't
> actually a threading issue: you don't need multiple threads to experience
> werid issues here. If you do strange things in a destructor then you can
> come up with confusing code even with a single thread.

I see.  What about

del a
a= Z

Then, can we say 'a=Z' is atomic?  At least, it eliminates the
destructor issue you raise.

> >> Other nasty things can happen if you use dictionaries from multiple
> >> threads. You cannot add or remove a dictionary key while iterating over
> >> a dictionary. This isn't normally a big issue, but as soon as you try to
> >> share the dictionary between threads you'll have to be careful never to
> >> iterate through it.
>
> > These aren't documented, IIRC.  Did you just discover them by trial
> > and error?
>
> It is documented, but I can't remember where for Python 2.x. For Python 3,
> PEP 3106 says: "As in Python 2.x, mutating a dict while iterating over it
> using an iterator has an undefined effect and will in most cases raise a
> RuntimeError exception. (This is similar to the guarantees made by the Java
> Collections Framework.)"

I infer that d.items() holds the GIL during the entire operation, and
it's safe to put in a thread.  It is merely using an iterator that is
unsafe.  (Python 3.0 removed d.items(), leaving only the iterator, I
understand.)

I'm looking at the code, and I don't see where the size is safely
checked.  That is, can't I sneak in an add and a remove during
iteration, so long as it doesn't catch me?

I'm looking at 'dict_traverse':
	while (PyDict_Next(op, &i, &pk, &pv)) {
		Py_VISIT(pk);
		Py_VISIT(pv);
	}

No locks are acquired here, though I might have missed acquiring the
GIL somewhere else.

In the OP's example, he wasn't changing the size of the dict.



More information about the Python-list mailing list