I have the following code:
import threading
import time
class TestWorker (threading.Thread):
def __init__(self, threadID, name):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
def run(self):
print "Starting " + self.name
time.sleep(20)
print "Exiting " + self.name
# how do I let the calling thread know it's done?
class TestMain:
def __init__(self):
pass
def do_work(self):
thread = TestWorker(1, "Thread-1")
thread.start()
def do_something_else(self):
print "Something else"
def on_work_done(self):
print "work done"
How can I let the main thread know that the TestWorker has finished (call on_work_done()), without blocking calls to do_something_else() as thread.join() would?
You can give your thread instance an optional callback function to call when it's finished.
Note I added a Lock to prevent concurrent printing (which does block).
print_lock = threading.Lock() # Prevent threads from printing at same time.
class TestWorker(threading.Thread):
def __init__(self, threadID, name, callback=lambda: None):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.callback = callback
def run(self):
with print_lock:
print("Starting " + self.name)
time.sleep(3)
with print_lock:
print("Exiting " + self.name)
self.callback()
class TestMain:
def __init__(self):
self.work_done = False
def do_work(self):
thread = TestWorker(1, "Thread-1", self.on_work_done)
thread.start()
def do_something_else(self):
with print_lock:
print("Something else")
def on_work_done(self):
with print_lock:
print("work done")
self.work_done = True
main = TestMain()
main.do_work()
while not main.work_done:
main.do_something_else()
time.sleep(.5) # do other stuff...
print('Done')
Output:
Starting Thread-1
Something else
Something else
Something else
Something else
Something else
Something else
Exiting Thread-1
work done
Done
import queue
import threading
class SThread(threading.Thread, queue.Queue):
def __init__(self, queue_out: object):
threading.Thread.__init__(self)
queue.Queue.__init__(self)
self.queue_out = queue_out
self.setDaemon(True)
self.start()
def run(self):
print('Thread start')
while True:
cmd = self.get()
if cmd is None:
break # exit thread
self.queue_out.put(cmd['target'](*cmd.get('args', ())), **cmd.get('kwargs', {}))
self.task_done()
print('Thread stop')
def testFn(a):
print('+ %s' % a)
return a
if __name__ == '__main__':
print('main 1')
# init
queue_out = queue.Queue()
thread = SThread(queue_out)
# in
for a in range(5): thread.put(dict(target=testFn, args=(a,)))
thread.put(None)
print('main 2')
# out
while True:
try:
print('- %s' % queue_out.get(timeout=3))
except queue.Empty:
break
print('main 3')
OUT:
main 1
Thread start
main 2
+ 0
+ 1
+ 2
+ 3
+ 4
Thread stop
- 0
- 1
- 2
- 3
- 4
main 3
import threading
dt = {}
threading.Thread(target=dt.update, kwargs=dict(out=123)).start()
while 'out' not in dt:
print('out' in dt)
print(dt)
Related
I tried to get the example 1 Raising exceptions in a python thread using PyThreadState_SetAsyncExc() from geeksforgeeks
Different ways to kill a Thread running.
But for some reason the thread does not terminate.
I use python3.6
here is the source-code
# Python program raising
# exceptions in a python
# thread
import threading
import ctypes
import time
class thread_with_exception(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
# target function of the thread class
try:
while True:
print('running ' + self.name)
finally:
print('ended')
def get_id(self):
# returns id of the respective thread
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def raise_exception(self):
thread_id = self.get_id()
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')
t1 = thread_with_exception('Thread 1')
t1.start()
time.sleep(2)
t1.raise_exception()
t1.join()
Does anybody have an idea why the thread is not terminated with the raise signal?
Wrap thread_id in ctypes.c_long as:
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(thread_id),
ctypes.py_object(SystemExit))
Here's my simple example code:
# -*- coding: utf-8 -*-
import multiprocessing
import time
def some_function():
return "hello!"
def get_address_type(address):
return 'tcp'
class PseudoThing():
def __init__(self, address):
self.address = address
self.server = None
def attach(self):
if get_address_type(self.address) == 'tcp':
ip, port = self.address.split(':', 1)
self.server = some_function()
else:
self.server = some_function()
def worker(self):
print("in worker!")
for i in range(10):
print("I'm a worker doing worker things...")
self.attach()
if self.server:
return True
time.sleep(0.2)
def parent(self):
print("I'm a parent doing parent things!")
def start_process(self):
p = multiprocessing.Process(target=self.worker)
print("starting process")
p.start()
return p
def main():
nvr = PseudoThing("kitty")
p = nvr.start_process()
p.join()
print("__name__ = {}".format(__name__))
if __name__ == "__main__":
print("doing main!")
main()
However, nothing is getting printed out from worker, as I would expect. Instead, this is the output of the program:
__name__ = __main__
doing main!
starting process
I am suspecting this has something to do with the class structure (I recall having issues with this on Python 2), but I am not entirely sure. Where can I add more print statements to figure what's going on? Where are things going wrong?
I write this code to obtain a simple loading animation. The animation is executed by a dedicated thread but it does not work, only print a single view when the program ends. I do not see the problem.
import threading
import time
import sys
done = False
class myThread (threading.Thread):
def __init__(self, threadID, name):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
def run(self):
print ("Starting " + self.name)
print_loadbar(self.name)
print ("Exiting " + self.name)
def print_loadbar(threadName):
while True:
if done :
break
sys.stdout.write('\r[--- ]')
sys.stdout.flush()
time.sleep(0.5)
sys.stdout.write('\r[ ---]')
sys.stdout.flush()
time.sleep(0.5)
thread1 = myThread(1, "Thread-1")
thread1.start()
#MAIN THREAD
c=0
while c<10 :
time.sleep(1)
c=c+1
done=True
I'm trying to create loop of threads and so far code is good. But I have problem when thread exits because of some exception.
Now I'm trying to figure out how to start additional thread after one thread exits because of exception. I did browse around but I didn't find any example that would work for this complex code. Any help would be great!
If thread stopped and queue is not empty restart stopped thread and continue with rest of the list.
This is my code:
some_list = [1,2,3,4,5,6,7,8]
exitFlag = 0
class threads():
#staticmethod
def process_data(threadName, q,queueLock):
workQueue = q
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print "%s processing %s" % (threadName, data)
else:
queueLock.release()
sleep(1)
def run_threads(self):
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = some_list
queueLock = threading.Lock()
workQueue = Queue.Queue(1000000)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue,queueLock)
thread.start()
threads.append(thread)
threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads it's time to exit
global exitFlag
exitFlag = 1
# Wait for all threads to complete
for t in threads:
t.join()
print "Exiting Main Thread"
class myThread (threading.Thread,threads):
def __init__(self, threadID, name, q,queueLock):
self.thread = threading.Thread(target=self.run)
threading.Thread.__init__(self,target=self.run)
self.threadID = threadID
self.queueLock = queueLock
self.name = name
self.q = q
def run(self):
print "Starting " + self.name
threads.process_data(self.name, self.q,self.queueLock)
print "Exiting " + self.name
threads().run_threads()
Something like this should work:
...
# Wait for queue to empty
while not workQueue.empty():
for (i, t) in enumerate(threads):
if not t.is_alive():
print("Recreating thread " + t.name)
thread = myThread(threadID, threadList[i], workQueue,queueLock)
thread.start()
threads[i] = thread
threadID += 1
...
I would advice putting the thread-starting code into some method, as it will now be duplicated and hard to maintain.
The problem here is that you might "loose" the data that was popped from queue by the fatal thread.
My code is processing some parallel perforce tasks while showing a progress bar and letting user to terminate the job whenever he wants, the problem is when user clicks the close button the thread function is not being killed but the lock is released and the main UI thread is being unlocked.
The p4.run_sync() is not terminating when Cancel button is clicked.
def P4SyncLibrary(args, que):
syncType = args[0]
view = args[1]
p4 = P4CreateConnection(disable_tmp_cleanup=True)
try:
p4.run_sync(view)
except P4Exception:
for e in p4.errors:
print "SyncError: - %s" %e
p4.disconnect()
que.put(None)
class CreateJob(QtGui.QDialog):
def __init__(self, thread, args):
QtGui.QDialog.__init__(self)
self.ui=Ui_ProgressBar()
self.ui.setupUi(self)
self.ui.cancel.clicked.connect(self.closeEvent)
self.ui.cancel.setIcon(QtGui.QIcon(QtGui.QPixmap("%s/delete.xpm" %resources)))
self.threadControl = ThreadControl(thread=thread, args=args)
self.connect(self.threadControl, QtCore.SIGNAL("__updateProgressBar(int)"), self.__updateProgressBar)
self.threadControl.finished.connect(self.closeEvent)
self.threadControl.start()
#QtCore.pyqtSlot(int)
def __updateProgressBar(self,val):
self.ui.progressBar.setValue(val)
self.setWindowTitle("Processing: {0}%".format(val))
def closeEvent(self, QCloseEvent=None):
if self.threadControl.isRunning():
self.threadControl.stop()
self.threadControl.wait()
if QCloseEvent: QtGui.QDialog.closeEvent(self, QCloseEvent)
else: self.close()
def getResults(self):
return self.threadControl.resultDict
class ThreadControl(QtCore.QThread):
stopFlag = 0
def __init__(self, thread=None, args=None):
super(ThreadControl, self).__init__()
self.args = args
self.thread = thread
self.resultDict = []
def run(self):
threads = {}
queue = multiprocessing.Queue()
for arg in self.args:
process = multiprocessing.Process(target=self.thread, args=(arg, queue))
process.start()
threads[process] = 1 ## ACTIVE thread
# WAIT TILL ALL PROCESSES COMPLETE
completedThreads = 0
total = len(threads.keys())
while completedThreads != total:
if self.stopFlag:
for t in threads.keys():
if threads[t] == 1:
t.terminate()
t.join()
threads[t] = 0
completedThreads += 1
else:
for t in threads.keys():
if self.stopFlag: break ## Process threads termination
elif threads[t] == 1 and not t.is_alive():
threads[t] = 0
completedThreads += 1
self.resultDict.append(queue.get())
self.emit(QtCore.SIGNAL('__updateProgressBar(int)'),(completedThreads*100)/total)
sleep(0.5) ## Prevent CPU from overloading
def stop(self):
self.stopFlag=1
a job is being created using instance of CreateJob
CreateJob(thread=P4SyncLibrary, args=P4Libraries).exec_()
The only solution I could give is to pass p4 object to calling thread as argument so that p4 server connection can disconnect when user wants to cancel the job.
def P4SyncLibrary(p4, args, que):
syncType = args[0]
view = args[1]
try:
p4.run_sync(view)
except P4Exception:
for e in p4.errors:
print "SyncError: - %s" %e
que.put(None)
class ThreadControl(QtCore.QThread):
...
def run(self):
threads = {}
queue = multiprocessing.Queue()
for arg in self.args:
connection = P4CreateConnection(disable_tmp_cleanup=True)
if connection.connected():
process = multiprocessing.Process(target=self.thread, args=(connection, arg, queue))
process.start()
threads[process] = {
'isAlive': True,
'connection': connection
}
# WAIT TILL ALL PROCESSES COMPLETE
completedThreads = 0
total = len(threads.keys())
while completedThreads != total:
if self._stop:
for t in threads.keys():
if threads[t]['isAlive']:
threads[t]['connection'].disconnect()
t.terminate()
t.join()
threads[t]['isAlive'] = False
completedThreads += 1
else:
for t in threads.keys():
if self._stop: break ## Process threads termination
elif threads[t]['isAlive'] and not t.is_alive():
threads[t]['connection'].disconnect()
threads[t]['isAlive'] = False
completedThreads += 1
self.results.append(queue.get())
self.emit(QtCore.SIGNAL('__updateProgressBar(int)'),(completedThreads*100)/total)
sleep(0.5) ## Prevent CPU from overloading