Now the pool itself: a queue of pending jobs and a way to run them. Each submit stashes a job and hands back a future; run drains the queue, executing jobs and fulfilling their futures.
submit(fn, *args, **kwargs): enqueue the job, return its Futurerun(): process every queued job in FIFO order, calling fn(*args, **kwargs) and storing the result on its futureNothing executes until run() is called. That's what makes it a pool and not a plain function call.
from collections import deque
class Future:
def __init__(self):
self._done = False
self._result = None
self._exc = None
def set_result(self, value):
self._result = value
self._done = True
def set_exception(self, exc):
self._exc = exc
self._done = True
def done(self):
return self._done
def result(self):
if not self._done:
raise RuntimeError("future is not done")
if self._exc is not None:
raise self._exc
return self._result
class ThreadPool:
def __init__(self, num_workers):
self.num_workers = num_workers
self._queue = deque() # (future, fn, args, kwargs)
def submit(self, fn, *args, **kwargs):
"""Enqueue a job and return its Future."""
# TODO:
# fut = Future()
# self._queue.append((fut, fn, args, kwargs))
# return fut
pass
def run(self):
"""Run every queued job in FIFO order, fulfilling each future."""
# TODO: while self._queue:
# fut, fn, args, kwargs = self._queue.popleft()
# fut.set_result(fn(*args, **kwargs))
pass
The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.
Now the pool itself: a queue of pending jobs and a way to run them. Each submit stashes a job and hands back a future; run drains the queue, executing jobs and fulfilling their futures.
submit(fn, *args, **kwargs): enqueue the job, return its Futurerun(): process every queued job in FIFO order, calling fn(*args, **kwargs) and storing the result on its futureNothing executes until run() is called. That's what makes it a pool and not a plain function call.
Press Run Tests or ββ© to check your solution.