In our solutions, the sg instance is indeed being passed around in every function that needs it. It is not so bad, there are some fat functions but one more argument is okay.
This way the lifetime of sg is limited to the worker’s scope - once worker returns, the instance will be garbage collected.
There are several ways to make this “simpler” if you wish.
One is Dan’s example with a class that remembers an instance and uses it in its own methods with
self.sg. This is sort of the standard OOP way of structuring things.
A related idea is that this object can itself be a callable and passed as worker. e.g.
def __init__(self, sg=None):
# pretty much everything from Dan's example
tasks = self.find_tasks(...)
# do something with tasks
thread = threading.Thread(target=SGWorker())
Another option is partial application (the functions still have sg as their first argument, but you pass it just once to
find_tasks_with_sg = functools.partial(find_tasks, sg)
# now use multiple times
This is more powerful than it seems at first.