Classes to run Producers (iterators) and Consumers (coroutines) introducing concurrency.
There are many libraries to create pipelines with stages running in separate processes, a nice one is parallelpipe, but this library does something different. It runs the entire upstream iterable (the Producer) in a background thread or process, while the downstream consumption code stays in the foreground. It's a more coarse library but easier to integrate since things keep looking like normal generators.
pip install concurrent-iteratorThere are three modules, each providing a Producer, a Consumer, and
(for thread) a MultiProducer:
dummy: non-concurrent implementation, useful for testing or as a drop-in replacement when concurrency is not needed.thread: uses a background thread to run the iterable or coroutine. Useful for IO-bound work.process: uses a background process to run the iterable or coroutine. Useful for CPU or IO-bound work. It has the complications of dealing with processes (different memory spaces, logging, etc). For logging, modulemultiprocessing-loggingcan be used.
All Producer, MultiProducer, and Consumer classes implement the
IProducer and IConsumer interfaces defined in concurrent_iterator
(the package root). They support:
- Iteration (
__next__/__iter__) — Producers yield values from the background iterable. - Sending (
Consumer.send) — Consumers forward values to the background coroutine. - Explicit close (
close()) — stops the background worker and releases resources. - State inspection (
closedproperty) — checks whether the worker has been closed. - Context manager (
withstatement) —close()is called automatically on exit.
import time
from concurrent_iterator.thread import Producer
def slow_generator(count):
for i in range(count):
time.sleep(0.5) # Simulate a slow producer
yield i
# Pre-calculate up to 5 values while the main thread consumes them.
with Producer(slow_generator(10), maxsize=5) as items:
for item in items:
print(item) # Do some time-consuming taskIn this example, while the main thread processes each item,
slow_generator continues running in a background thread and
pre-calculates up to 5 values.
The with statement ensures the background thread is properly cleaned
up.
from concurrent_iterator.thread import Consumer
def worker():
while True:
value = yield # Coroutine protocol
# ... do work with value ...
coro = worker()
next(coro) # Prime the coroutine
with Consumer(coro, maxsize=5) as consumer:
consumer.send("a")
consumer.send("b")
# close() is called automatically on exit, which also closes the coroutine.The Consumer runs the coroutine in a background thread. send()
enqueues values; if the value cannot be enqueued within timeout
(default 0, never blocks), it raises
concurrent_iterator.WillNotConsume, signaling the caller to retry.
Both Producers and Consumers can be used as context managers. When the
with block exits, close() is called automatically, ensuring
background threads or processes are terminated even on exceptions.
from concurrent_iterator.thread import Producer
with Producer(range(100), maxsize=10) as items:
for item in items:
if item > 5:
break # close() is still called by the with blockExceptions raised by generators are forwarded to the main thread. When a
generator raises, the producer forwards the exception, re-raising it from
__next__(). After an exception, the producer is terminated and raises
StopIteration on subsequent calls.
For process.Producer, if the child process terminates unexpectedly,
__next__() raises RuntimeError("Child process died."), and becomes
closed.
Limitations:
process.Producer/process.Consumerwith generators, coroutines and other unpicklable objects require theforkstart method. Python 3.14 changes the default on Linux fromforktoforkserver, andspawn/forkservercannot pickle generators (TypeError: cannot pickle 'generator' object). In those casesprocess.Producer/process.ConsumerraisesRuntimeErrorwith a clear message. Usethread.Producer/thread.Consumer, a picklable iterable (e.g.iter([1,2,3])), or passmp_context=multiprocessing.get_context('fork')whereforkis available (Linux).