I'm trying to enqueue a list of lists in a process (in the function named proc below), and then have the process terminate itself after I call event.set(). My function proc always finishes, judging by the printout, but the process itself is still going. I can get this to work if I make the number of lists enqueued in a call to put lower (batchperq variable) (or the size of each nested list smaller).
import multiprocessing as mp
import queue
import numpy as np
import time
def main():
trainbatch_q = mp.Queue(10)
batchperq = 50
event = mp.Event()
tl1 = mp.Process(target=proc,
args=( trainbatch_q, 20, batchperq, event))
tl1.start()
time.sleep(3)
event.set()
tl1.join()
print("Never printed..")
def proc(batch_q, batch_size, batchperentry, the_event):
nrow = 100000
i0 = 0
to_q = []
while i0 < nrow:
rowend = min(i0 + batch_size,nrow)
somerows = np.random.randint(0,5,(rowend-i0,2))
to_q.append(somerows.tolist())
if len(to_q) == batchperentry:
print("adding..", i0, len(to_q))
while not the_event.is_set():
try:
batch_q.put(to_q, block=False)
to_q = []
break
except queue.Full:
time.sleep(1)
i0 += batch_size
print("proc finishes")
When I do a keyboard interrupt, I get the trace below... what could the "lock" it's trying to acquire be? Something to do with the queue?
Traceback (most recent call last):
File "/software/Anaconda3-5.0.0.1-el7-x86_64/lib/python3.6/multiprocessing/process.py", line 252, in _bootstrap
util._exit_function()
File "/software/Anaconda3-5.0.0.1-el7-x86_64/lib/python3.6/multiprocessing/util.py", line 322, in _exit_function
_run_finalizers()
File "/software/Anaconda3-5.0.0.1-el7-x86_64/lib/python3.6/multiprocessing/util.py", line 262, in _run_finalizers
finalizer()
File "/software/Anaconda3-5.0.0.1-el7-x86_64/lib/python3.6/multiprocessing/util.py", line 186, in __call__
res = self._callback(*self._args, **self._kwargs)
File "/software/Anaconda3-5.0.0.1-el7-x86_64/lib/python3.6/multiprocessing/queues.py", line 198, in _finalize_join
thread.join()
File "/software/Anaconda3-5.0.0.1-el7-x86_64/lib/python3.6/threading.py", line 1056, in join
self._wait_for_tstate_lock()
File "/software/Anaconda3-5.0.0.1-el7-x86_64/lib/python3.6/threading.py", line 1072, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
The reason your process is never exiting is because you're never telling it to exit. I added a return to the end of your function and your process appears to exit correctly now.
def proc(batch_q, batch_size, batchperentry, the_event):
nrow = 100000
i0 = 0
to_q = []
while i0 < nrow:
rowend = min(i0 + batch_size,nrow)
somerows = np.random.randint(0,5,(rowend-i0,2))
to_q.append(somerows.tolist())
if len(to_q) == batchperentry:
print("adding..", i0, len(to_q))
while not the_event.is_set():
try:
batch_q.put(to_q, block=False)
to_q = []
break
except queue.Full:
time.sleep(1)
i0 += batch_size
print("proc finishes")
return # Added this line, You can have it return whatever is most relevant to you.
Here is the full program I ran including my changes to make it exit successfully.
import multiprocessing as mp
import queue
import numpy as np
import random
import time
def main():
trainbatch_q = mp.Queue(10)
batchperq = 50
event = mp.Event()
tl1 = mp.Process(target=proc,
args=( trainbatch_q, 20, batchperq, event))
print("Starting")
tl1.start()
time.sleep(3)
event.set()
tl1.join()
print("Never printed..")
def proc(batch_q, batch_size, batchperentry, the_event):
nrow = 100000
i0 = 0
to_q = []
while i0 < nrow:
rowend = min(i0 + batch_size,nrow)
somerows = np.random.randint(0,5,(rowend-i0,2))
to_q.append(somerows.tolist())
if len(to_q) == batchperentry:
print("adding..", i0, len(to_q))
while not the_event.is_set():
try:
batch_q.put(to_q, block=False)
to_q = []
break
except queue.Full:
time.sleep(1)
i0 += batch_size
print("proc finishes")
return # Added this line, You can have it return whatever is most relevant to you.
if __name__ == "__main__":
main()
Hope this is helps.
Related
I'm working on a project where a data capture system sends EMG data to a python script through an Arduino Uno's serial port and the python script classifies the user's intent by processing a chunk of that data. The data capture system sends samples at 250Hz and I require my python script to not miss samples the capture system is sending while classifying.
For this I want two parallelly running processes:
A process continuously capturing the data and buffering it. (using PySerial) - running every 0.004s
A process taking data from the buffer, preprocess it and classifying it. - this process approximately takes 0.03 seconds to run per loop excluding data acquisition
I don't know how to proceed with this. I've tried and read through a lot of articles but to no avail.
I tried threading, but apparently threading is not true parallelism, so I gave up trying to make it work.
I tried using multiprocessing but the multiprocessing module doesn't like pickling pyserial.
Here's what I tried: (It's probably full of mistakes, I hacked it up but I expected some output, also this is not indefinitely buffering, but I'm trying to get at least the first buffer printed)
Driver Code:
import GatherData as gd
import serial.tools.list_ports
from multiprocessing import Pool
if __name__ == '__main__':
arr = []
serialInst = serial.Serial()
ports = serial.tools.list_ports.comports()
portList = []
for Port in ports:
portList.append(str(Port))
print(str(Port))
val = input("Select Port: COM")
for x in range(0,len(portList)):
if(portList[x].startswith("COM"+str(val))):
portVar = "COM"+str(val)
print(portList[x])
serialInst.baudrate = 115200
serialInst.port = portVar
serialInst.open()
p = Pool(6) # Number of concurrent processes
arr = p.starmap(gd.collect, (arr, serialInst)) # Start all processes
p.map(gd.printnum(arr))
p.close()
p.join()
GatherData.py:
import serial.tools.list_ports
import numpy as np
import pandas as pd
# import Dependencies as dp
def collect(array,serialInst):
ch = 4
if not array:
return makearr()
else:
nextw = array[1:]
temparr = []
if (serialInst.in_waiting):
packet = serialInst.readline()
# now = time.time()
line_as_list = packet.split(b'\t')
#print(line_as_list)
for i in range(1,ch+1):
try:
rand1=line_as_list[i]
#print("rand1", rand1)
rand1List = rand1.split(b'\n')
rand1f = float(rand1List[0])
temparr.append(rand1f)
#print(temparr)
except IndexError:
return collect(array) #add pca
except ValueError:
return collect(array) #add pca
nextw.append(temparr)
print(nextw)
return nextw
def makearr(serialInst):
final = []
counter = 0
while(counter<50):
temparr = []
if (serialInst.in_waiting):
packet = serialInst.readline()
line_as_list = packet.split(b'\t')
flag = 0
for i in range(1,5):
try:
rand1=line_as_list[i]
#print("rand1", rand1)
rand1List = rand1.split(b'\n')
rand1f = float(rand1List[0])
temparr.append(rand1f)
except IndexError:
flag = 1
continue
except ValueError:
flag = 1
continue
if(flag == 0):
final.append(temparr)
counter=counter+1
return final
def printnum(array):
while True:
if array:
print(len(array), end='\r')
The errors:
COM8 - Arduino Uno (COM8)
Select Port: COM8
COM8 - Arduino Uno (COM8)
Traceback (most recent call last):
File "D:\anaconda\envs\tf\lib\site-packages\spyder_kernels\py3compat.py", line 356, in compat_exec
exec(code, globals, locals)
File "c:\users\indra\documents\igibup.py", line 34, in <module>
arr = p.starmap(gd.collect, (arr, serialInst)) # Start all processes
File "D:\anaconda\envs\tf\lib\multiprocessing\pool.py", line 276, in starmap
return self._map_async(func, iterable, starmapstar, chunksize).get()
File "D:\anaconda\envs\tf\lib\multiprocessing\pool.py", line 657, in get
raise self._value
File "D:\anaconda\envs\tf\lib\multiprocessing\pool.py", line 431, in _handle_tasks
put(task)
File "D:\anaconda\envs\tf\lib\multiprocessing\connection.py", line 206, in send
self._send_bytes(_ForkingPickler.dumps(obj))
File "D:\anaconda\envs\tf\lib\multiprocessing\reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
ValueError: ctypes objects containing pointers cannot be pickled
I don't understand how to proceed.
I have a dummy example, I want to apply multiprocessing in it. Consider a scenario where you have a stream of numbers(which I call frame) incoming one by one. And I want to assign it to any single process that is available currently. So I am creating 4 processes that are running a while loop, seeing if any element in queue, than apply function on it.
The problem is that when I join it, it gets stuck in any while loop, even though I close the while loop before it. But somehow it gets stuck inside it.
Code:
# step 1, 4 processes
import multiprocessing as mp
import os
import time
class MpListOperations:
def __init__(self):
self.results_queue = mp.Manager().Queue()
self.frames_queue = mp.Manager().Queue()
self.flag = mp.Manager().Value(typecode='b',value=True)
self.list_nums = list(range(0,5000))
def process_list(self):
print(f"Process id {os.getpid()} started")
while self.flag.value:
# print(self.flag.value)
if self.frames_queue.qsize():
self.results_queue.put(self.frames_queue.get()**2)
def create_processes(self, no_of_processes = mp.cpu_count()):
print("Creating Processes")
self.processes = [mp.Process(target=self.process_list) for _ in range(no_of_processes)]
def start_processes(self):
print(f"starting processes")
for process in self.processes:
process.start()
def join_process(self):
print("Joining Processes")
while True:
if not self.frames_queue.qsize():
self.flag.value=False
print("JOININNG HERE")
for process in self.processes:
exit_code = process.join()
print(exit_code)
print("BREAKING DONE")
break
def stream_frames(self):
print("Streaming Frames")
for frame in self.list_nums:
self.frames_queue.put(frame)
if __name__=="__main__":
start = time.time()
mp_ops = MpListOperations()
mp_ops.create_processes()
mp_ops.start_processes()
mp_ops.stream_frames()
mp_ops.join_process()
print(time.time()-start)
Now if I add a timeout parameter in join, even 0, i.e exit_code = process.join(0) it works. I want to understand in this scenario, if this code is correct, what should be the value of timeout? Why is it working with timeout and not without it? What is the proper way to implement multiprocessing with it?
If you look at the documentation for a managed queue you will see that the qsize method only returns an approximate size. I would therefore not use it for testing when all the items have been taken of the frames queue. Presumably you want to let the processes run until all frames have been processed. The simplest way I know would be to put N sentinel items on the frames queue after the actual frames have been put where N is the number of processes getting from the queue. A sentinel item is a special value that cannot be mistaken for an actual frame and signals to the process that there are no more items for it to get from the queue (i.e. a quasi end-of-file item). In this case we can use None as the sentinel items. Each process then just continues to do get operations on the queue until it sees a sentinel item and then terminates. There is therefore no need for the self.flag attribute.
Here is the updated and simplified code. I have made some other minor changes that have been commented:
import multiprocessing as mp
import os
import time
class MpListOperations:
def __init__(self):
# Only create one manager process:
manager = mp.Manager()
self.results_queue = manager.Queue()
self.frames_queue = manager.Queue()
# No need to convert range to a list:
self.list_nums = range(0, 5000)
def process_list(self):
print(f"Process id {os.getpid()} started")
while True:
frame = self.frames_queue.get()
if frame is None: # Sentinel?
# Yes, we are done:
break
self.results_queue.put(frame ** 2)
def create_processes(self, no_of_processes = mp.cpu_count()):
print("Creating Processes")
self.no_of_processes = no_of_processes
self.processes = [mp.Process(target=self.process_list) for _ in range(no_of_processes)]
def start_processes(self):
print("Starting Processes")
for process in self.processes:
process.start()
def join_processes(self):
print("Joining Processes")
for process in self.processes:
# join returns None:
process.join()
def stream_frames(self):
print("Streaming Frames")
for frame in self.list_nums:
self.frames_queue.put(frame)
# Put sentinels:
for _ in range(self.no_of_processes):
self.frames_queue.put(None)
if __name__== "__main__":
start = time.time()
mp_ops = MpListOperations()
mp_ops.create_processes()
mp_ops.start_processes()
mp_ops.stream_frames()
mp_ops.join_processes()
print(time.time()-start)
Prints:
Creating Processes
Starting Processes
Process id 28 started
Process id 29 started
Streaming Frames
Process id 33 started
Process id 31 started
Process id 38 started
Process id 44 started
Process id 42 started
Process id 45 started
Joining Processes
2.3660173416137695
Note for Windows
I have modified method start_processes to temporarily set attribute self.processes to None:
def start_processes(self):
print("Starting Processes")
processes = self.processes
# Don't try to pickle list of processes:
self.processes = None
for process in processes:
process.start()
# Restore attribute:
self.processes = processes
Otherwise under Windows we get a pickle error trying to serialize/deserialize a list of processes containing two or more multiprocessing.Process instances. The error is "TypeError: cannot pickle 'weakref' object." This can be demonstrated with the following code where we first try to pickle a list of 1 process and then a list of 2 processes:
import multiprocessing as mp
import os
class Foo:
def __init__(self, number_of_processes):
self.processes = [mp.Process(target=self.worker) for _ in range(number_of_processes)]
self.start_processes()
self.join_processes()
def start_processes(self):
processes = self.processes
for process in self.processes:
process.start()
def join_processes(self):
for process in self.processes:
process.join()
def worker(self):
print(f"Process id {os.getpid()} started")
print(f"Process id {os.getpid()} ended")
if __name__== "__main__":
foo = Foo(1)
foo = Foo(2)
Prints:
Process id 7540 started
Process id 7540 ended
Traceback (most recent call last):
File "C:\Booboo\test\test.py", line 26, in <module>
foo = Foo(2)
File "C:\Booboo\test\test.py", line 7, in __init__
self.start_processes()
File "C:\Booboo\test\test.py", line 13, in start_processes
process.start()
File "C:\Program Files\Python38\lib\multiprocessing\process.py", line 121, in start
self._popen = self._Popen(self)
File "C:\Program Files\Python38\lib\multiprocessing\context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "C:\Program Files\Python38\lib\multiprocessing\context.py", line 327, in _Popen
return Popen(process_obj)
File "C:\Program Files\Python38\lib\multiprocessing\popen_spawn_win32.py", line 93, in __init__
reduction.dump(process_obj, to_child)
File "C:\Program Files\Python38\lib\multiprocessing\reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
TypeError: cannot pickle 'weakref' object
Process id 18152 started
Process id 18152 ended
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Program Files\Python38\lib\multiprocessing\spawn.py", line 116, in spawn_main
exitcode = _main(fd, parent_sentinel)
File "C:\Program Files\Python38\lib\multiprocessing\spawn.py", line 126, in _main
self = reduction.pickle.load(from_parent)
EOFError: Ran out of input
The target loop is stuck in the get() method of your loop. This is because multiple processes could see that the queue wasn't empty, but only 1 of them was able to get the last item. The remaining processes are waiting for the next item to be available from the queue.
You might need to add a Lock when you are reading the size of the Queue object And getting the object of that queue.
Or alternatively, you avoid reading the size of the queue by simply using the queue.get() method with a timeout that allows us to check the flag regularly
import queue
TIMEOUT = 1 # seconds
class MpListOperations:
#[...]
def process_list(self):
print(f"Process id {os.getpid()} started")
previous = self.flag.value
while self.flag.value:
try:
got = self.frames_queue.get(timeout=TIMEOUT)
except queue.Empty:
pass
else:
print(f"Gotten {got}")
self.results_queue.put(got**2)
_next = self.flag.value
if previous != _next:
print(f"Flag change: {_next}")
$ python ./test_mp.py
Creating Processes
starting processes
Process id 36566 started
Streaming Frames
Process id 36565 started
Process id 36564 started
Process id 36570 started
Process id 36567 started
Gotten 0
Process id 36572 started
Gotten 1
Gotten 2
Gotten 3
Process id 36579 started
Gotten 4
Gotten 5
Gotten 6
Process id 36583 started
Gotten 7
# [...]
Gotten 4997
Joining Processes
Gotten 4998
Gotten 4999
JOININNG HERE
Flag change: False
Flag change: False
Flag change: False
Flag change: False
Flag change: False
Flag change: False
Flag change: False
Flag change: False
Exit code : None
Exit code : None
Exit code : None
Exit code : None
Exit code : None
Exit code : None
Exit code : None
Exit code : None
BREAKING DONE
1.4375360012054443
Alternatively, using a multiprocessing.Pool object:
def my_func(arg):
time.sleep(0.002)
return arg**2
def get_input():
for i in range(5000):
yield i
time.sleep(0.001)
if __name__=="__main__":
start = time.time()
mp_pool = mp.Pool()
result = mp_pool.map(my_func, get_input())
mp_pool.close()
mp_pool.join()
print(len(result))
print(f"Duration: {time.time()-start}")
Giving:
$ python ./test_mp.py
5000
Duration: 6.847279787063599
I'm a beginner learning python and ran into some issues while using locks during multiprocessing.
I get an exit code 0 and the right answer but still have some sort of error message which I really don't fully understand. Here's The code I've written-
import time
import multiprocessing
def deposit(balance):
for i in range(100):
time.sleep(0.01)
lck.acquire()
balance.value += 1
lck.release()
def withdraw(balance):
for i in range(100):
time.sleep(0.01)
lck.acquire()
balance.value -= 1
lck.release()
if __name__ == '__main__':
balance = multiprocessing.Value('i', 200)
lck = multiprocessing.Lock()
d = multiprocessing.Process(target=deposit, args=(balance,))
w = multiprocessing.Process(target=withdraw, args=(balance,))
d.start()
w.start()
d.join()
w.join()
print(balance.value)
and here's the error I get
`Process Process-1:
Traceback (most recent call last):
File "C:\Users\rahul\AppData\Local\Programs\Python\Python39\lib\multiprocessing\process.py", line
315, in _bootstrap
self.run()
File "C:\Users\rahul\AppData\Local\Programs\Python\Python39\lib\multiprocessing\process.py", line
108, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\rahul\PycharmProjects\pythonProject\LearningPython.py", line 10, in deposit
lck.acquire()
NameError: name 'lck' is not defined
Process Process-2:
Traceback (most recent call last):
File "C:\Users\rahul\AppData\Local\Programs\Python\Python39\lib\multiprocessing\process.py", line
315, in _bootstrap
self.run()
File "C:\Users\rahul\AppData\Local\Programs\Python\Python39\lib\multiprocessing\process.py", line
108, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\rahul\PycharmProjects\pythonProject\LearningPython.py", line 17, in withdraw
lck.acquire()
NameError: name 'lck' is not defined
200
Process finished with exit code 0
The problem here is that lck is out of scope of your child processes. Global variables aren't shared across processes. Try passing the lock into the processes.
Alternatively use threads as suggested in kahn's answer. They are much friendlier and still work fine in this case.
import time
import multiprocessing
def deposit(balance,lck):
for i in range(100):
time.sleep(0.01)
lck.acquire()
balance.value += 1
lck.release()
def withdraw(balance,lck):
for i in range(100):
time.sleep(0.01)
lck.acquire()
balance.value -= 1
lck.release()
if __name__ == '__main__':
balance = multiprocessing.Value('i', 200)
lck = multiprocessing.Lock()
d = multiprocessing.Process(target=deposit, args=(balance,lck))
w = multiprocessing.Process(target=withdraw, args=(balance,lck))
d.start()
w.start()
d.join()
w.join()
print(balance.value)
Because deposit and withdraw run in other processes.In their view,the process is not __main__,so the if statement is not executed and lck is not defined.
Try Run
import os
import multiprocessing
def deposit(balance):
print(os.getpid(),__name__)
def withdraw(balance):
print(os.getpid(),__name__)
if __name__ == '__main__':
print(os.getpid(), __name__)
balance = multiprocessing.Value('i', 200)
lck = multiprocessing.Lock()
d = multiprocessing.Process(target=deposit, args=(balance,))
w = multiprocessing.Process(target=withdraw, args=(balance,))
d.start()
w.start()
d.join()
w.join()
In my case ,it shows
19604 __main__
33320 __mp_main__
45584 __mp_main__
Your code can run if you put lck = multiprocessing.Lock() outside if.But I'm sure it's not what you want.
You should use threading instead of multiprocess in this case,and have a look at difference between multi-thread and multi-process.
I am using threads. One thread as a producer thread and the other thread as the consumer thread. The threads enqueue and dequeue from the same queue (q1). The producer function seems to be working fine. However, the consumer thread (dequeue function) is giving me error. Below is my code
def PacketProducer(Threadnum, numTicks, onProb, offProb, q):
l=0
onState = True
packetId = 0
for i in range(numTicks):
x = generator(onProb, offProb, 'OnOff', onState)
if(x==True):
onState = True
pkt = packet
pkt.srcID = Threadnum
pkt.pktId = packetId
q.put(pkt)
print(pkt.pktId, ' ', pkt.srcID)
l = l+1
packetId = packetId + 1
else:
onState = False
time.sleep(1)
#print(x)
print(l/numTicks, ' threadNum', Threadnum)
return
def PacketConsumer(q): #dequeues and enqueues and returns the packet
while True:
if not q.empty():
pkt = q.get()
print('dequeuing Packet', ' ', pkt.pktId, pkt.srcID)
time.sleep(1)
return
if __name__ == '__main__':
numTicks = 40
q1 = Queue()
pkt = packet
x = threading.Thread(target=PacketProducer, args=(1, numTicks, 0.7, 0.7, q1))
#y = threading.Thread(target=PacketProducer, args=(2, numTicks, 0.6, 0.4, q1))
t = threading.Thread(target=PacketConsumer, args=(q1))
t.start()
x.start()
#y.start()
x.join()
# y.join()
t.join()
It gives me the following error:
Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Users\Syed\AppData\Local\Programs\Python\Python37-32\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "C:\Users\Syed\AppData\Local\Programs\Python\Python37-32\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
TypeError: PacketConsumer() argument after * must be an iterable, not Queue
A single item tuple must have a , in it to distinguish it as such, otherwise it's just grouping parenthesis.
Change this line:
t = threading.Thread(target=PacketConsumer, args=(q1,))
Can someone let me know what is wrong in my code below that implements the producer-consumer problem in python. I am using Python 3.4
import threading
from threading import Thread
from collections import deque
import time
maxSize = 4 # maximum size of the queue
q = deque([])
cur = 0 # current value to push into the queue
lk = threading.Lock()
cvP = threading.Condition(lk) # condition object for consumer
cvC = threading.Condition(lk) # condition object for producer
class Producer:
def run(self):
global maxSize, q, cur
while True:
with cvP:
while len(q) >= maxSize:
print( "Queue is full and size = ", len(q) )
cvC.notify() # notify the Consumer
cvP.wait() # put Producer to wait
q.append(cur)
print("Produced ", cur)
cur = cur + 1
cvC.notify() # notify the Consumer
class Consumer:
def run(self):
global maxSize, q, cur
while True:
with cvC:
while len(q) == 0:
print( "Queue is empty and size = ", len(q) )
cvP.notify() # notify the Producer
cvC.wait() # put Consumer to wait
x = q.popleft()
print("Consumed ", x)
time.sleep(1)
cvP.notify() # notify the Producer
p = Producer()
c = Consumer()
pThread = Thread( target=p.run(), args=())
cThread = Thread( target=c.run(), args=())
pThread.start()
cThread.start()
pThread.join()
cThread.join()
The program output:
Produced 0
Produced 1
Produced 2
Produced 3
Queue is full and size = 4
Then it got stuck. When terminating the program, I got:
Traceback (most recent call last):
File "path/t.py", line 47, in <module>
pThread = Thread( target=p.run(), args=())
File "path/t.py", line 22, in run
cvP.wait()
File "/usr/lib/python3.4/threading.py", line 289, in wait
waiter.acquire()
KeyboardInterrupt
The Producer seemed not "nofity" the consumer. Can someone let me know why?
Many thanks in advance!
The locking and unlocking are fine, but you probably want to specify 'run' as the target and not 'run()'
pThread = Thread( target=p.run, args=())
cThread = Thread( target=c.run, args=())
:-)
Explanation: lets simplify
def foo():
# ..
# Foo will run on a new thread
Thread(target=foo)
# foo() is run before the thread is created, and its return
# value is the target for the Thread call.
Thread(target=foo())
You can see in the stack trace that it never went beyond line 47, which is
pThread = Thread( target=p.run(), args=())
Which is the same as
x = p.run()
pThread = Thread(x, args=())