Why is `gevent.spawn` different from` threading.Thread () `from monkeypatched?

While double checking that the threading.Condition correctly fixed by a monkey, I noticed that the monkey carried by threading.Thread(…).start() behaves differently than gevent.spawn(…) .

Consider:

 from gevent import monkey; monkey.patch_all() from threading import Thread, Condition import gevent cv = Condition() def wait_on_cv(x): cv.acquire() cv.wait() print "Here:", x cv.release() # XXX: This code yields "This operation would block forever" when joining the first thread threads = [ gevent.spawn(wait_on_cv, x) for x in range(10) ] """ # XXX: This code, which seems semantically similar, works correctly threads = [ Thread(target=wait_on_cv, args=(x, )) for x in range(10) ] for t in threads: t.start() """ cv.acquire() cv.notify_all() print "Notified!" cv.release() for x, thread in enumerate(threads): print "Joining", x thread.join() 

Please note that two comments starting with XXX .

When using the first line (with gevent.spawn ), the first thread.join() throws an exception:

 Notified!
 Joining 0
 Traceback (most recent call last):
   File "foo.py", line 30, in 
     thread.join ()
   File "... / gevent / greenlet.py", line 291, in join
     result = self.parent.switch ()
   File "... / gevent / hub.py", line 381, in switch
     return greenlet.switch (self)
 gevent.hub.LoopExit: This operation would block forever

However, Thread(…).start() (second block) everything works as expected.

Why was that? What is the difference between gevent.spawn() and Thread(…).start() ?

+8
python multithreading gevent
source share
1 answer

What happens in your code is that the greens you created in the threads list have not been executed yet, because gevent will not trigger the context switch until you do this explicitly in your code using gevent.sleep() and one way or another implicitly, by calling a function that blocks, for example, semaphore.wait() or by input, etc., to see that you can insert a print before cv.wait() and see that it is called only after calling cv.notify_all() :

 def wait_on_cv(x): cv.acquire() print 'acquired ', x cv.wait() .... 

This way, it will be easy for your code to insert something that will bring up the context switch after creating the green list, for example:

 ... threads = [ gevent.spawn(wait_on_cv, x) for x in range(10) ] gevent.sleep() # Trigger a context switch ... 

Note: I am not familiar with gevent , so I don’t know if this is the right thing to do :)

Thus, all greens will have the opportunity to execute, and each of them will bring up a context switch when they call cv.wait() , and, on average, they will register them themselves for the conditions of the waiters, so that when cv.notify_all() is called notify all green .

NTN

+5
source share

All Articles