On Sat, Oct 13, 2012 at 11:46 AM, Nick Coghlan
In a Deferred callback, on the other hand, you know the only things that are going to run are functions you call. In so far as it's possible, what happens is under control of one function only. Less pretty, but no potential race conditions:
def add(result): counter.value = counter.value + result getResult().addCallback(add)
This is not the same code you wrote above in the generator version. The callback equivalent of the code you wrote is this:
bound_value = counter.value def add(result): counter.value = bound_value + result getResult().addCallback(add)
True, so, let's look at this version. First, notice that it's more convoluted than the version I wrote above; i.e. you have to go out of your way to write race conditiony code. Second, and much more important, when reading it it's obvious that you're getting and setting counter.value at different times! Whereas in the generator version you have to think about it. The generator version has you naturally writing code where things you thought are happening at the same time are actually happening very far apart; the Deferred code makes it clear which pieces of code happen separately, and so you're much more likely to notice these sort of bugs. The generator version isn't magic, people still need to know what
they're doing to properly benefit from the cooperative multithreading.
I agree. And that's exactly the dimension in which Deferreds are superior to cooperative multithreading; people don't have to think about race conditions as much, which is hard enough in general. At least when you're using Deferreds, you can tell by reading the code which chunks of code can happen at different times, and the natural idioms of Python don't *encourage* race conditions as they do with yield syntax. -Itamar