473,739 Members | 4,265 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Suggested generator to add to threading module.

Found myself needing serialised access to a shared generator from
multiple threads. Came up with the following

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next

I considered suggesting it for itertools, but really it's thread
specific so I am suggesting it for the threading module.

Andrae Muys
Jul 18 '05 #1
17 2436
Andrae Muys wrote:
I considered suggesting it for itertools, but really it's thread
specific so I am suggesting it for the threading module.


If you are willing to contribute this function, please submit
a patch to sf.net/projects/python, including documentation changes,
and test cases.

Regards,
Martin

Jul 18 '05 #2
In article <79************ **************@ posting.google. com>,
Andrae Muys <am***@shortech .com.au> wrote:

Found myself needing serialised access to a shared generator from
multiple threads. Came up with the following

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next


I'm not sure this is generic enough to go in the standard library.
Usually, I'd recommend that someone wanting this functionality consider
other options in addition to this (such as using Queue.Queue()).
--
Aahz (aa**@pythoncra ft.com) <*> http://www.pythoncraft.com/

A: No.
Q: Is top-posting okay?
Jul 18 '05 #3
Andrae Muys wrote:
Found myself needing serialised access to a shared generator from
multiple threads. Came up with the following

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next


Is there any reason why the lock is not shared among threads?
From the looks of this, it doesn't synchronize anything
between different threads. Am I missing something?

Kind regards,
Ype

email at xs4all.nl
Jul 18 '05 #4
On Fri, Jan 16, 2004 at 08:42:36PM +0100, Ype Kingma wrote:
Found myself needing serialised access to a shared generator from
multiple threads. Came up with the following

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next


Is there any reason why the lock is not shared among threads?
From the looks of this, it doesn't synchronize anything

between different threads. Am I missing something?


Yes, I think so. You'd use the same "serialise" generator object in
multiple threads, like this:

p = seralise(produc er_generator())
threads = [thread.start_ne w(worker_thread , (p,))
for t in range(num_worke rs)]

Jeff

Jul 18 '05 #5
[Andrae Muys]
Found myself needing serialised access to a shared generator from
multiple threads. Came up with the following

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next

[Ype Kingma] Is there any reason why the lock is not shared among threads?
From the looks of this, it doesn't synchronize anything
between different threads. Am I missing something?

[Jeff Epler] Yes, I think so. You'd use the same "serialise" generator object in
multiple threads, like this:

p = seralise(produc er_generator())
threads = [thread.start_ne w(worker_thread , (p,))
for t in range(num_worke rs)]


Hmm. I think Ype is right: the above code does not correctly serialise
access to a generator.

The above serialise function is a generator which wraps a generator.
This presumably is in order to prevent the wrapped generators .next()
method being called simultaneously from multiple threads (which is
barred: PEP 255: "Restrictio n: A generator cannot be resumed while it
is actively running")

http://www.python.org/peps/pep-0255.html

However, the above implementation re-creates the problem by using an
outer generator to wrap the inner one. The outer's .next() method will
then potentially be called simultaneously by multiple threads. The
following code illustrates the problem

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import time
import thread
import threading

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next

def squares(n):
i = 1
while i < n:
yield i*i
i = i+1

def worker_thread(i ter, markers):
markers[thread.get_iden t()] = 1
results = [] ; clashes = 0
while 1:
try:
results.append( iter.next())
except StopIteration:
break
except ValueError, ve:
if str(ve) == "generator already executing":
clashes = clashes + 1
del markers[thread.get_iden t()]
print "Thread %5s: %d results: %d clashes." % (thread.get_ide nt(),\
len(results), clashes)

numthreads = 10 ; threadmarkers = {}
serp = serialise(squar es(100))
threads = [thread.start_ne w_thread(worker _thread,\
(serp, threadmarkers)) for t in xrange(numthrea ds)]
while len(threadmarke rs.keys()) > 0:
time.sleep(0.1)
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

I believe that the following definition of serialise will correct the
problem (IFF I've understood the problem correctly :-)

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import time
import thread
import threading

class serialise:
"Wrap a generator in an iterator for thread-safe access"

def __init__(self, gen):
self.lock = threading.Lock( )
self.gen = gen

def __iter__(self):
return self

def next(self):
self.lock.acqui re()
try:
return self.gen.next()
finally:
self.lock.relea se()

def squares(n):
i = 1
while i < n:
yield i*i
i = i+1

def worker_thread(i ter, markers):
markers[thread.get_iden t()] = 1
results = [] ; clashes = 0
while 1:
try:
results.append( iter.next())
except StopIteration:
break
except ValueError, ve:
if str(ve) == "generator already executing":
clashes = clashes + 1
del markers[thread.get_iden t()]
print "Thread %5s: %d results: %d clashes." % (thread.get_ide nt(),\
len(results), clashes)

numthreads = 10 ; threadmarkers = {}
serp = serialise(squar es(100))
threads = [thread.start_ne w_thread(worker _thread,\
(serp, threadmarkers)) for t in xrange(numthrea ds)]
while len(threadmarke rs.keys()) > 0:
time.sleep(0.1)
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Also, I don't know if I'm happy with relying on the fact that the
generator raises StopIteration for *every* .next() call after the
actual generated sequence has ended. The above code depends on the
exhausted generator raising StopIteration in every thread. This seems
to me the kind of thing that might be python-implementation specific.
For example, the original "Simple Generators" specification, PEP 255,
makes no mention of expected behaviour of generators when multiple
calls are made to the its .next() method after the iteration is
exhausted. That I can see anyway? Am I wrong?

http://www.python.org/peps/pep-0255.html

regards,

--
alan kennedy
------------------------------------------------------
check http headers here: http://xhaus.com/headers
email alan: http://xhaus.com/contact/alan
Jul 18 '05 #6
Aahz wrote:
In article <79************ **************@ posting.google. com>,
Andrae Muys <am***@shortech .com.au> wrote:
Found myself needing serialised access to a shared generator from
multiple threads. Came up with the following

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next

I'm not sure this is generic enough to go in the standard library.
Usually, I'd recommend that someone wanting this functionality consider
other options in addition to this (such as using Queue.Queue()).


While I fully appreciate the importance of a Queue.Queue in implementing
a producer/consumer task relationship, this particular function provides
serialised access to a *passive* data-stream. With the increasing
sophistication of itertools and I feel there maybe an argument for
supporting shared access to a generator.

Anyway I thought it was worth offering as a possible bridge between the
itertools and threading modules. If I'm mistaken, then it's no major loss.

Andrae

Jul 18 '05 #7
Alan,

you wrote:
[Andrae Muys]
Found myself needing serialised access to a shared generator from
multiple threads. Came up with the following

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next
[Ype Kingma] Is there any reason why the lock is not shared among threads?
From the looks of this, it doesn't synchronize anything
between different threads. Am I missing something?

[Jeff Epler]
Yes, I think so. You'd use the same "serialise" generator object in
multiple threads, like this:

p = seralise(produc er_generator())
threads = [thread.start_ne w(worker_thread , (p,))
for t in range(num_worke rs)]


Hmm. I think Ype is right: the above code does not correctly serialise
access to a generator.


Well, I just reread PEP 255, and I can assure you a was missing something...
The above serialise function is a generator which wraps a generator.
This presumably is in order to prevent the wrapped generators .next()
method being called simultaneously from multiple threads (which is
barred: PEP 255: "Restrictio n: A generator cannot be resumed while it
is actively running")

http://www.python.org/peps/pep-0255.html

However, the above implementation re-creates the problem by using an
outer generator to wrap the inner one. The outer's .next() method will
then potentially be called simultaneously by multiple threads. The
I agree (after rereading the PEP.)
following code illustrates the problem

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import time
import thread
import threading

def serialise(gen):
lock = threading.Lock( )
while 1:
lock.acquire()
try:
next = gen.next()
finally:
lock.release()
yield next

def squares(n):
i = 1
while i < n:
yield i*i
i = i+1

def worker_thread(i ter, markers):
markers[thread.get_iden t()] = 1
results = [] ; clashes = 0
while 1:
try:
results.append( iter.next())
except StopIteration:
break
except ValueError, ve:
if str(ve) == "generator already executing":
clashes = clashes + 1
del markers[thread.get_iden t()]
print "Thread %5s: %d results: %d clashes." % (thread.get_ide nt(),\
len(results), clashes)

numthreads = 10 ; threadmarkers = {}
serp = serialise(squar es(100))
threads = [thread.start_ne w_thread(worker _thread,\
(serp, threadmarkers)) for t in xrange(numthrea ds)]
while len(threadmarke rs.keys()) > 0:
time.sleep(0.1)
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

I believe that the following definition of serialise will correct the
problem (IFF I've understood the problem correctly :-)

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import time
import thread
import threading

class serialise:
"Wrap a generator in an iterator for thread-safe access"

def __init__(self, gen):
self.lock = threading.Lock( )
self.gen = gen

def __iter__(self):
return self

def next(self):
self.lock.acqui re()
try:
return self.gen.next()
finally:
self.lock.relea se()
Looks like a candidate for inclusion in a standard library to me.
def squares(n):
i = 1
while i < n:
yield i*i
i = i+1

def worker_thread(i ter, markers):
markers[thread.get_iden t()] = 1
results = [] ; clashes = 0
while 1:
try:
results.append( iter.next())
except StopIteration:
break
except ValueError, ve:
if str(ve) == "generator already executing":
clashes = clashes + 1
del markers[thread.get_iden t()]
print "Thread %5s: %d results: %d clashes." % (thread.get_ide nt(),\
len(results), clashes)

numthreads = 10 ; threadmarkers = {}
serp = serialise(squar es(100))
threads = [thread.start_ne w_thread(worker _thread,\
(serp, threadmarkers)) for t in xrange(numthrea ds)]
while len(threadmarke rs.keys()) > 0:
time.sleep(0.1)
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Also, I don't know if I'm happy with relying on the fact that the
generator raises StopIteration for *every* .next() call after the
actual generated sequence has ended. The above code depends on the
exhausted generator raising StopIteration in every thread. This seems
to me the kind of thing that might be python-implementation specific.
For example, the original "Simple Generators" specification, PEP 255,
makes no mention of expected behaviour of generators when multiple
calls are made to the its .next() method after the iteration is
exhausted. That I can see anyway? Am I wrong?


Quoting from PEP 234:
http://www.python.org/peps/pep-0234.html

"Once a particular iterator object has raised StopIteration, will
it also raise StopIteration on all subsequent next() calls?
....
Resolution: once StopIteration is raised, calling it.next()
continues to raise StopIteration."

Thanks to all for the help,

Ype

Jul 18 '05 #8
[Andrae Muys]
> Found myself needing serialised access to a shared generator from
> multiple threads. Came up with the following
>
> def serialise(gen):
> lock = threading.Lock( )
> while 1:
> lock.acquire()
> try:
> next = gen.next()
> finally:
> lock.release()
> yield next
[Ype Kingma] Is there any reason why the lock is not shared among threads?
From the looks of this, it doesn't synchronize anything
between different threads. Am I missing something?
[Jeff Epler] Yes, I think so. You'd use the same "serialise" generator object in
multiple threads, like this:

p = seralise(produc er_generator())
threads = [thread.start_ne w(worker_thread , (p,))
for t in range(num_worke rs)]

[Alan Kennedy] Hmm. I think Ype is right: the above code does not correctly serialise
access to a generator.
[Ype Kingma]
Well, I just reread PEP 255, and I can assure you a was missing
something...
Ype,

Ah: I see now. You thought it didn't work, but for a different reason
than the one I pointed out. You thought that the lock was not shared
between threads, though as Jeff pointed out, it is if you use it the
right way.

But it still doesn't work.

[Alan Kennedy] I believe that the following definition of serialise will correct the
problem (IFF I've understood the problem correctly :-)

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import time
import thread
import threading

class serialise:
"Wrap a generator in an iterator for thread-safe access"

def __init__(self, gen):
self.lock = threading.Lock( )
self.gen = gen

def __iter__(self):
return self

def next(self):
self.lock.acqui re()
try:
return self.gen.next()
finally:
self.lock.relea se()
[Ype Kingma] Looks like a candidate for inclusion in a standard library to me.
Well, maybe :-)

To be honest, I don't have the time to write test cases, docs and
patches. So I think I'll just leave it for people to find in the
Google Groups archives ...

[Alan Kennedy] Also, I don't know if I'm happy with relying on the fact that the
generator raises StopIteration for *every* .next() call after the
actual generated sequence has ended. The above code depends on the
exhausted generator raising StopIteration in every thread. This seems
to me the kind of thing that might be python-implementation specific.
For example, the original "Simple Generators" specification, PEP 255,
makes no mention of expected behaviour of generators when multiple
calls are made to the its .next() method after the iteration is
exhausted. That I can see anyway? Am I wrong?


[Ype Kingma] Quoting from PEP 234:
http://www.python.org/peps/pep-0234.html

"Once a particular iterator object has raised StopIteration, will
it also raise StopIteration on all subsequent next() calls?
...
Resolution: once StopIteration is raised, calling it.next()
continues to raise StopIteration."


Yes, that clears the issue up nicely. Thanks for pointing that out.

So the same code will run correctly in Jython 2.3 and IronPython
(awaited with anticipation).

regards,

--
alan kennedy
------------------------------------------------------
check http headers here: http://xhaus.com/headers
email alan: http://xhaus.com/contact/alan
Jul 18 '05 #9
[Subject line changed to allow thread to be found more easily in
google-groups]

Alan Kennedy <al****@hotmail .com> wrote in message news:<40******* ********@hotmai l.com>...
[Alan Kennedy]
I believe that the following definition of serialise will correct the
problem (IFF I've understood the problem correctly :-)

It does look like the following version will work, I was too focused
on synchronising the underlying generator, and forgot that my code
also needed to be re-entrant. Thanks for catching my mistake.
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import time
import thread
import threading

class serialise:
"Wrap a generator in an iterator for thread-safe access"

def __init__(self, gen):
self.lock = threading.Lock( )
self.gen = gen

def __iter__(self):
return self

def next(self):
self.lock.acqui re()
try:
return self.gen.next()
finally:
self.lock.relea se()


[Ype Kingma]
Looks like a candidate for inclusion in a standard library to me.


Well, maybe :-)

To be honest, I don't have the time to write test cases, docs and
patches. So I think I'll just leave it for people to find in the
Google Groups archives ...


Andrae Muys
Jul 18 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
1894
by: Andrea Manzini | last post by:
Hi all, I'm a python newbie with a little problem; my need is to share an incrementing counter between threads; i.e. every time I create a new thread (which handle a single tcp/ip connection), I must assign an unique numeric ID to it, without reuse old numbers... In example: thread-1 id: 1 thread-2 id: 2 thread-3 id: 3 thread-100 id: 100 thread-n id: n
4
2341
by: Wai Yip Tung | last post by:
I'm attempting to turn some process than uses callback to return result into a more user friendly generator. I'm hitting some road block so any pointer would be appriciated. Let say there is an existing method producer(x, cb). It calls the supplied cb() every time when there is data available. The definititon of cb should be: def cb(data)
13
2714
by: Varun | last post by:
Hi Friends, Department of Information Technology, Madras Institute of Technology, Anna University, India is conducting a technical symposium, Samhita. As a part of samhita, an Online Programming Contest is scheduled on Sunday, 27 Feb 2005. This is the first Online Programming Contest in India to support Python !!!!. Other languages supported are C and C++.
3
1494
by: David Harrison | last post by:
I am working on an application on Mac OS X that calls out to python via PyImport_ImportModule(). I find that if the imported module creates and starts a python thread, the thread seems to be killed when the import of the module is complete. Is this expected? Does python have to be in control to allow threads to run? Would it be better to arrange things such that the file is processed using PyRun_SimpleFile? David S. Harrison
3
1532
by: Paul Rubin | last post by:
As I understand it, generators are supposed to run til they hit a yield statement: import time def f(): print 1 time.sleep(3) for i in range(2,5): yield i
7
3594
by: Kirk McDonald | last post by:
Let's say I have a function that takes a callback function as a parameter, and uses it to describe an iteration: def func(callback): for i in : callback(i) For the sake of argument, assume the iteration is something more interesting than this which relies on the callback mechanism. The function is an existing interface, and I cannot change it.
1
1651
by: Schüle Daniel | last post by:
Hello, I came up with this algorithm to generate all permutations it's not the best one, but it's easy enough # lst = list with objects def permute3(lst): tmp = lenlst = len(lst) def permute(perm, level):
14
1221
by: castironpi | last post by:
I'm actually curious if there's a way to write a generator function (not a generator expression) in C, or what the simplest way to do it is... besides link the Python run-time.
0
1224
by: Edwin.Madari | last post by:
1. check out the Caveats for thread module: http://docs.python.org/lib/module-thread.html Threads interact strangely with interrupts: the KeyboardInterrupt exceptionwill be received by an arbitrary thread. (When the signal module is available, interrupts always go to the main thread.) i.e., all threads (including main) to catch interrupt exceptions, and propagate that information to other threads. 2. since there is no way to interrupt...
0
8969
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8792
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
8215
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
6754
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6054
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4570
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
4826
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
2748
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2193
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.