dotancohen
dotancohen

Reputation: 31521

Sending arguments to Python threads blocks other threads

Consider this test application for passing arguments to Python threads:

#!/usr/bin/python3

from pprint import pprint
import signal
import sys
import threading


class CallThreads(threading.Thread):

    def __init__(self, target, *args):
        self._target = target
        threading.Thread.__init__(self)
        target(*args)

def main(argv):
    phrases = ['hello', 'goodbye']
    num = 0

    for phrase in phrases:
        num += 1
        thread_handler = CallThreads(someFunction, phrase, num)
        thread_handler.daemon = True
        thread_handler.start()

    return True


def someFunction(a, b):
    print("Hi: "+str(a)+" and "+str(b))
    return True


def signal_handler(signal, frame):
    print(["Got SIGINT!"])
    sys.exit(0)


if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    main(sys.argv)

In it's current state, it seems as though the for phrase in phrases loop is waiting for the thread to finish before starting another thread. That is, if someFunction() takes a long time to complete, then the then next thread will not start until the previous thread returns. Why is this, and how can I work around it while still sending arguments to the threads?

Edit: I've tried saving the args array in self._args in the constructor, and then calling self._target(*self._args) or self._target(self._args) in def run (self):. This actually works in Python 2, but not in Python 3. What should I do?

Edit: It seems that the problem is that in Python 3, the run method cannot access the private variables. That is, for the following improved code:

def __init__(self, target, *args):
    self._args = args
    threading.Thread.__init__(self)

def run(self):
    someFunction(*self._args)

Note the following output:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python3.3/threading.py", line 639, in _bootstrap_inner
    self.run()
  File "./test.py", line 19, in run
    someFunction(*self._args)
TypeError: someFunction() missing 2 required positional arguments: 'a' and 'b'

And adding a pprint(self._args) to the run() method indeed shows that the tuple returned is empty. However, changing the variables to non-private works! The following code runs fine:

def __init__(self, target, *args):
    self.target = target
    self.args = args
    threading.Thread.__init__(self)

def run(self):
    self.target(*self.args)

Therefore, I can use the application with public variables in Python 3. However, is there any way to use private variables in the CallThreads class, as in Python 2?

Thanks!

Upvotes: 1

Views: 940

Answers (1)

E.Z.
E.Z.

Reputation: 6661

The problem is that you call target(args) on the constructor of CallThreads.

Therefore the following call blocks until CallThreads.__init__() is finished:

thread_handler = CallThreads(someFunction, phrase, num)

Update: One possibility could be the following:

class CallThreads(threading.Thread):

    def __init__(self, *args):
        self._args   = args
        threading.Thread.__init__(self)

    def run(self):
        someFunction(*self._args)

Upvotes: 4

Related Questions