Because sometimes it's more practical than designing a solution around queues, I would like to write a simple wrapper to make an iterator thread safe. So far, I had inspiration from these topics and came up with two ideas:
class LockedIterator(object):
def __init__(self, it):
self._lock = threading.Lock()
self._it = it.__iter__()
if hasattr(self._it, 'close'):
def close(self):
with self._lock:
self._it.close()
self.__setattr__('close', close)
def __iter__(self):
return self
def next(self):
with self._lock:
return self._it.next()
What I don't like about it, is that it gets a bit lengthy if I have to specify all possible methods - okay, I can't - such as the special case for generators. Also, I might have some other iterator with even more specific methods that have now become hidden.
class LockedIterator(object):
def __init__(self, it):
self._lock = threading.Lock()
self._it = it.__iter__()
def __getattr__(self, item):
attr = getattr(self._it, item)
if callable(attr):
def hooked(*args, **kwargs):
with self._lock:
return attr(*args, **kwargs)
setattr(self, item, hooked)
return hooked
This is more concise, but it can only intercept calls, and not, for example, direct property changes. (Those properties are now hidden to prevent problems.) More importantly, it makes it so that Python does no longer recognize my object as an iterator!
What is the best way of making this work for all iterators (or even better: all objects), without creating a leaky abstraction? I'm not too worried about locking when it's not necessary, but if you can come up with a solution that circumvents that, great!
If a class or a program has immutable state then the class is necessarily thread-safe. Similarly, the shared state in an application where the same thread mutates the state using an operation that translates into an atomic bytecode instruction can be safely read by multiple reader threads.
Iterators are still not threadsafe. The solution to this iteration problem will be to acquire the collection's lock when you need to iterate over it, which we'll talk about in a future reading.
It's not thread-safe; simultaneous calls may interleave, and mess with the local variables.
Like the other have said, Python objects are mostly thread-safe. Although you will need to use Locks in order to protect an object in a place that require it to go through multiple changes before being usable again.
First, are you aware of the GIL? Attempts to write multi-threaded Python typically end up in slower run-time than with a straightforward single-threaded version.
Your first attempt at making access to an iterator thread-safe seems quite reasonable. You can make it a bit more readable by using a generator:
def locked_iter(it):
it = iter(it)
lock = threading.Lock()
while True:
try:
with lock:
value = next(it)
except StopIteration:
return
yield value
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With