473,387 Members | 1,578 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Simple thread-safe counter?

I'd like to have a function (or other callable object) that returns
0, 1, 2, etc. on repeated calls. That is:

print f() # prints 0
print f() # prints 1
print f() # prints 2
# etc.

There should never be any possibility of any number getting returned
twice, or getting skipped over, even if f is being called from
multiple threads.

What's the simplest and most natural way to do this? I can think of a
few but am not sure that they work. And I can think of some ways that
are sure to work, but are messier than I'd like.
Jul 18 '05 #1
16 10608
[Paul Rubin]
I'd like to have a function (or other callable object) that returns
0, 1, 2, etc. on repeated calls. That is:

print f() # prints 0
print f() # prints 1
print f() # prints 2
# etc.

There should never be any possibility of any number getting returned
twice, or getting skipped over, even if f is being called from
multiple threads.

What's the simplest and most natural way to do this? I can think of a
few but am not sure that they work. And I can think of some ways that
are sure to work, but are messier than I'd like.


The GIL is your friend here:

import itertools
f = itertools.count().next

A similar thing can be done with xrange. But either way sucks if you
call it often enough to exceed the size of a Python short int
(platform C long). The obvious way with an explicit mutex doesn't
have that problem.
Jul 18 '05 #2

Paul> I'd like to have a function (or other callable object) that
Paul> returns 0, 1, 2, etc. on repeated calls.
...
Paul> There should never be any possibility of any number getting
Paul> returned twice, or getting skipped over, even if f is being called
Paul> from multiple threads.

How about (untested):

import Queue

counter = Queue.Queue()
counter.put(0)
def f():
i = counter.get()
counter.put(i+1)
return i

Obviously, if you want multiple counters for some reason a little
information hiding with a class would help (also untested):

import Queue

class Counter:
def __init__(self, start=0):
self.counter = Queue.Queue()
self.counter.put(start)

def __call__(self):
i = self.counter.get()
self.counter.put(i+1)
return i

Skip
Jul 18 '05 #3
Tim Peters <ti********@gmail.com> writes:
The GIL is your friend here:

import itertools
f = itertools.count().next
Thanks, I was hoping something like this would work but was not sure I
could rely on it.
A similar thing can be done with xrange. But either way sucks if you
call it often enough to exceed the size of a Python short int
(platform C long). The obvious way with an explicit mutex doesn't
have that problem.


Xrange, of course :). I don't need to exceed the size of a short int,
so either of these should work fine. I wonder what measures the Pypy
implementers will take (if any) to make sure these things keep
working, but for now I won't worry about it.

Out of interest, are the above guaranteed to work under Jython? What
I'm doing right now is a short-term thing that will only have to run
under CPython, but I like to do things the right way when I can.
Jul 18 '05 #4
Skip Montanaro wrote:
Paul> I'd like to have a function (or other callable object) that
Paul> returns 0, 1, 2, etc. on repeated calls.
...
Paul> There should never be any possibility of any number getting
Paul> returned twice, or getting skipped over, even if f is being called
Paul> from multiple threads.

How about (untested):

import Queue

counter = Queue.Queue()
counter.put(0)
def f():
i = counter.get() I think you need:
i = counter.get(True)
for this to work; otherwise a race condition would raise an exception. counter.put(i+1)
return i


[snip]

This is, of course dependent upon counter.get() being guaranteed to be
thread safe. (I haven't found anything in the docs specifically relating
to that. Perhaps it's implicit?)

Thanks,
--ag

--
Artie Gold -- Austin, Texas
http://it-matters.blogspot.com (new post 12/5)
http://www.cafepress.com/goldsays
Jul 18 '05 #5
Skip Montanaro <sk**@pobox.com> writes:
How about (untested):

import Queue
counter = Queue.Queue()
counter.put(0)
def f():
i = counter.get()
counter.put(i+1)
return i


Hmmm, that's a bit messier than I hoped for, but it looks sure to work.

I think for my immediate requirement, I'm going to use xrange as Tim
suggested. However, I can't be sure that will work in non-GIL
implementations.
Jul 18 '05 #6
Tim Peters wrote in news:mailman.1223.1112417955.1799.python-
li**@python.org in comp.lang.python:
[Paul Rubin]
I'd like to have a function (or other callable object) that returns
0, 1, 2, etc. on repeated calls. That is:

print f() # prints 0
print f() # prints 1
print f() # prints 2
# etc.

There should never be any possibility of any number getting returned
twice, or getting skipped over, even if f is being called from
multiple threads.

What's the simplest and most natural way to do this? I can think of a
few but am not sure that they work. And I can think of some ways that
are sure to work, but are messier than I'd like.
The GIL is your friend here:


Yes but IIUC the GIL is only CPython, will this work with Jython,
IornPython etc ? (*)
import itertools
f = itertools.count().next


*) I'm either being rhetorical or stupid, not sure which :).

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 18 '05 #7
Artie Gold wrote:
Skip Montanaro wrote:
counter = Queue.Queue()
def f():
i = counter.get()


I think you need:
i = counter.get(True)


The default value for the "block" argument to Queue.get is True.
Jul 18 '05 #8
Paul Rubin wrote:
Skip Montanaro <sk**@pobox.com> writes:

How about (untested):

import Queue
counter = Queue.Queue()
counter.put(0)
def f():
i = counter.get()
counter.put(i+1)
return i


Hmmm, that's a bit messier than I hoped for, but it looks sure to work.

I think for my immediate requirement, I'm going to use xrange as Tim
suggested. However, I can't be sure that will work in non-GIL
implementations.

Hello,

Simple xrange stuff won't work in Jython as Sython is running
on a JVM which doesn't have a GIL kinda thing. If you wanna return a
sequential number then you'll need something like :

<PSEUDO CODE>

class COUNTER:
DOC : THIS WILL RETURN AN INCRMENTING LIST OF NUMBERS - THREAD SAFE.

FUNCTION INIT:
OBJRLOCK= RLOCK()
INTRETURNNUM = 0

FUNCTION NEXT
TRY{
RLOCK.AQUIRE
}FINALLY{
RLOCK.RELEASE
}

</PSUEDO CODE>

That basically all you'll need, if you make it iteratable of
whatever - you need to wrap the business end of what you are doing
around a recursive lock. Personally I dislike the GIL so I avoid
writing code that takes advantages of it.

Why psuedo code - this is similar to python code I know but it means
I'm not posting untested python code!!

Cheers,

Neil
Jul 18 '05 #9
Leif K-Brooks wrote:
Artie Gold wrote:
Skip Montanaro wrote:
counter = Queue.Queue()
def f():
i = counter.get()

I think you need:
i = counter.get(True)

The default value for the "block" argument to Queue.get is True.


Right. I misparsed the entry in the documentation:

"If optional args block is true and timeout is None (the default), block
if necessary..."

Thanks,
--ag

--
Artie Gold -- Austin, Texas
http://it-matters.blogspot.com (new post 12/5)
http://www.cafepress.com/goldsays
Jul 18 '05 #10
In article <ma**************************************@python.o rg>,
Skip Montanaro <sk**@pobox.com> wrote:

Obviously, if you want multiple counters for some reason a little
information hiding with a class would help (also untested):

import Queue

class Counter:
def __init__(self, start=0):
self.counter = Queue.Queue()
self.counter.put(start)

def __call__(self):
i = self.counter.get()
self.counter.put(i+1)
return i


This is one case where'd recommend using a plan RLock() instead of using
Queue -- the RLock() will be more efficient:

import threading

class Counter:
def __init__(self, start=0, increment=1):
self.counter = start
self.increment = increment
self.lock = threading.RLock()
def __call__(self):
self.lock.acquire()
self.counter += self.increment
i = self.counter
self.lock.release()
return i

There are several tricks one can use to squeeze further efficiency gains
from this, including using Lock() instead of RLock().
--
Aahz (aa**@pythoncraft.com) <*> http://www.pythoncraft.com/

"The joy of coding Python should be in seeing short, concise, readable
classes that express a lot of action in a small amount of clear code --
not in reams of trivial code that bores the reader to death." --GvR
Jul 18 '05 #11
aa**@pythoncraft.com (Aahz) writes:
This is one case where'd recommend using a plan RLock() instead of using
Queue -- the RLock() will be more efficient...


I'm starting to believe the GIL covers up an awful lot of sloppiness
in Python. I wonder if there could be a decorator approach:

@synchronized
def counter():
t = itertools.count()
while True:
yield t.next()

or better yet,

counter = synchronized(itertools.count())
Jul 18 '05 #12
[Paul Rubin]
I'm starting to believe the GIL covers up an awful lot of sloppiness
in Python.


The GIL is highly exploitable, and much of CPython does exploit it.

If you don't want to exploit it, that's fine: there was always an
obvious approach using an explicit mutex here, and the only thing
stopping you from using it is a desire to be clever. Exploiting the
GIL in CPython is clever; using an explicit mutex is utterly
straightforward. Pick your poison.
Jul 18 '05 #13
Am Samstag, 2. April 2005 22:28 schrieb Paul Rubin:
I'm starting to believe the GIL covers up an awful lot of sloppiness
in Python. I wonder if there could be a decorator approach:

@synchronized
def counter():
t = itertools.count()
while True:
yield t.next()


Of course there could:

def synchronized_iterator(f):
def wrapper(*args,**kwargs):
class iterator(object):
def __init__(self,f,args,kwargs):
self.iter = f(*args,**kwargs)
self.lock = threading.RLock()
def __iter__(self):
return self
def next(self):
self.lock.acquire()
try:
return self.iter.next()
finally:
self.lock.release()
return iterator(f,args,kwargs)
return wrapper

@synchronized_iterator
def create_counter():
t = itertools.count()
while True:
yield t.next()

or

counter = synchronized_iterator(itertools.count)

I used a class-based approach, as I don't want to destroy the semantics of
calling the returned wrapper(), which should've already instantiated the
wrapped generator object (which doesn't happen when making wrapper() a
generator itself).

--
--- Heiko.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQBCTyN0f0bpgh6uVAMRAhl4AJ9eityDJSuzkfywFGC0DG UOiz4hXACePWFP
fi1F0tFXekyTzWbvbHi8zh8=
=flQB
-----END PGP SIGNATURE-----

Jul 18 '05 #14
Am Sonntag, 3. April 2005 00:57 schrieb Heiko Wundram:
<snip>
or


Make that:

create_counter = syncronized_iterator(itertools.count)

and

counter = create_counter()

to create the actual counter regardless of iterator.

--
--- Heiko.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQBCTyQNf0bpgh6uVAMRAqdJAJ9Efwzkf9xNau6LaRgcOT KwOpz95ACfe1Y7
iHWep1RM157Dd/pvxlhtYEg=
=zRYe
-----END PGP SIGNATURE-----

Jul 18 '05 #15
Tim Peters <ti********@gmail.com> writes:
If you don't want to exploit it, that's fine: there was always an
obvious approach using an explicit mutex here, and the only thing
stopping you from using it is a desire to be clever. Exploiting the
GIL in CPython is clever; using an explicit mutex is utterly
straightforward. Pick your poison.


For my immediate requirement I'm going to use xrange as you suggested
(thanks!). It will work exactly as I want. I'd be uncomfortable
publishing such a program for long-term use by other people, but this
is just for a short-term hack (famous last words).

I think using an explicit mutex goes against Guido's quote mentioned
yesterday, about avoiding tedious code. But relying on the GIL
results in a brittle program that only works correctly in one Python
implementation. As use of other implementations becomes more
widespread, one of three things has to happen: 1) Python programs will
become uglier and more mistake-prone (from using explicit mutexes all
over the place); 2) Python programs will become unreliable (from
depending on a GIL that doesn't exist any more); or 3) Python will
acquire some new mechanisms for handling reliable synchronization
easily and cleanly, as other languages already have.

I think (3) is the preferable choice and it's reasonable to try to
figure out how such mechanisms should work.
Jul 18 '05 #16
Paul Rubin <http://ph****@NOSPAM.invalid> writes:
Tim Peters <ti********@gmail.com> writes:
The GIL is your friend here:

import itertools
f = itertools.count().next


Thanks, I was hoping something like this would work but was not sure I
could rely on it.
A similar thing can be done with xrange. But either way sucks if you
call it often enough to exceed the size of a Python short int
(platform C long). The obvious way with an explicit mutex doesn't
have that problem.


Xrange, of course :). I don't need to exceed the size of a short int,
so either of these should work fine. I wonder what measures the Pypy
implementers will take (if any) to make sure these things keep
working, but for now I won't worry about it.


Well, for now we don't support threads. Easy!

Cheers,
mwh
(no, really, this is for the future)

--
Two things I learned for sure during a particularly intense acid
trip in my own lost youth: (1) everything is a trivial special case
of something else; and, (2) death is a bunch of blue spheres.
-- Tim Peters, 1 May 1998
Jul 18 '05 #17

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

Similar topics

14
by: adeger | last post by:
Having trouble with my first forays into threads. Basically, the threads don't seem to be working in parallel (or you might say are blocking). I've boiled my problems to the following short code...
8
by: Mark | last post by:
Hi, I'm looking for some ideas on how to build a very simple Event processing framework in my C++ app. Here is a quick background ... I'm building a multithreaded app in C++ (on Linux) that...
1
by: ORC | last post by:
Hi, I'm looking for a way to implement a simple functionality in my serial port C# wrapper. I'm making a program that must run on both a Pocket PC and a "normal" PC. The application is quite...
3
by: Elliot Rodriguez | last post by:
Hi: I am writing a WinForm app that contains a DataGrid control and a StatusBar control. My goal is to update the status bar using events from a separate class, as well as some other simple...
5
by: sarge | last post by:
I would like to know how to perform simple multithreading. I had created a simple form to test out if I was multithreading properly, but got buggy results. Sometime the whole thig would lock up...
27
by: one man army | last post by:
Hi All- I am new to PHP. I found FAQTS and the php manual. I am trying this sequence, but getting 'no zip string found:'... PHP Version 4.4.0 $doc = new DomDocument; $res =...
10
by: Scooby | last post by:
Okay, I'm a noob at C# and microsoft's IDE, but have a fairly long history of programming (with, cough, Delphi). I'm writing a mobile app in C# with Visual Studio 2005. I think there must be...
5
by: Brian | last post by:
I have an file based asp.net application the creates a thread to do some background printing. It works fine but when the application is deployed on a web server, the following error occurs in the...
1
by: =?ISO-8859-1?Q?Tor_Erik_S=F8nvisen?= | last post by:
Hi, A while ago I asked a question on the list about a simple eval function, capable of eval'ing simple python constructs (tuples, dicts, lists, strings, numbers etc) in a secure manner:...
17
by: Chris M. Thomasson | last post by:
I use the following technique in all of my C++ projects; here is the example code with error checking omitted for brevity: _________________________________________________________________ /*...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
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,...
0
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...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

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.