ninesalt
ninesalt

Reputation: 4354

How to exit all thread when one returns

I have the following piece of code that is supposed to simulate mining bitcoin but I'm running each "node" on it's own thread. What I'm trying to do is let all nodes start mining at the same time but when one of them is done, the others should stop. For some reason this isn't working. What is currently happening is apparently only the first node is allowed to mine while the others do nothing.

    chosen = self.nodes
    q = Queue()

    # to simulate concurrent mining, each client will get their own thread
    # and when one of them finds a correct nonce, the others will stop

    for node in chosen:
        Thread(target=node.mine, args=(txdata, lastblock, q)).start()
        return q.get()

node.mine() simply tries finding a hash of a block and a nonce (which always starts from 0 for all nodes) that starts with 5 zeros. The way I'm making some nodes mine faster than others is that they each have a random number representing their power and in node.mine(), each node sleeps for 1/power seconds before they do anything.

Upvotes: 0

Views: 66

Answers (1)

bnaecker
bnaecker

Reputation: 6460

As was pointed out in the comments, the fact that only one thread is running at one time is because the q.get() call is inside the loop. This blocks until a result is ready, which effectively serializes the threads. Moving that outside the loop will start all threads before checking the queue.

However, this does not solve your problem of exiting other threads when one finishes. The "best" way to do this really depends on the structure of the node.mine() method.

For example, if it's a loop of some kind, generally like:

while True:
    relatively_fast_computation()

then you can add a threading.Event() or similar to the mix, and have each thread check whether the event is set instead of just infinitely looping.

while not event.is_set():
    relatively_fast_computation()

The main thread waits for a result on the queue, and when one is received, sets the event. The worker threads then notice this the next pass through the while loop and quit. This is probably the preferred way, as it allows the worker threads to properly exit, cleanup, close files, etc.

The issue here is that this will not work well if each pass through the while loop is very slow, and it won't work at all if the functions inside there truly block, as in network I/O. In that case, there are workarounds, but none of them sound great or particularly applicable to your situation.

Edit:

And as was also pointed out in the comments, if the node.mine() method is a pure CPython function, then threading here is probably useless. The GIL will prevent CPU-bound code like hashing from running more than one thread anyway. Use multiprocessing.

Upvotes: 1

Related Questions