多线程
直接创建Thread对象
import threadingimport timedef test():for i in range(5):print(threading.current_thread().name+' test ',i)time.sleep(1)thread = threading.Thread(target=test) # 比较常用的入参还有name, args(a,), kwargs{key:val,}thread.start()
自定义类继承Thread并重写方法run
import threadingimport timeclass TestThread(threading.Thread):def __init__(self,name=None):threading.Thread.__init__(self,name=name)def run(self):for i in range(5):print(threading.current_thread().name + ' test ', i)time.sleep(1)thread = TestThread(name='TestThread')thread.start()
阻塞
thread.join():添加阻塞(阻塞的是主线程,与其他子线程无关),只有在当前子线程的任务全部结束后,才会解除阻塞。
参考:https://www.cnblogs.com/cnkai/p/7504980.html
多线程的常见问题:
1.多线程的创建应该使用两个循环分别处理start和join函数,即可实现并发,并阻塞主线程;知道所有子线程结束后,再解除阻塞
参考:https://blog.csdn.net/brucewong0516/article/details/81050792
threads = [Thread() for i in range(5)]for thread in threads:thread.start()for thread in threads:thread.join()
- 主线程结束不意味着程序结束,之后所有线程全部结束才行
如果子线程存在死循环,即使主线程结束了,子线程也会一直运行,导致程序一直运行,无法退出;通过设置thread.setDaemon(True)守护线程,就可以避免出现这种情况
队列中的阻塞
queue.join(): 添加阻塞(同样阻塞的是主线程)
对于队列,执行一次 put 会让未完成任务 +1 ,但是执行 get 并不会让未完成任务 -1 ,需要使用 task_done 让未完成任务 -1 ,否则 join 就无法判断什么时候该解除阻塞(源码中有一个unfinished记录未完成任务数,而task_done执行后就会让其-1,join则是根据这个变量来判断的)
参考:https://www.cnblogs.com/dbf-/p/11118628.html
备注:queue是线程安全的
可以通过运行下面这个程序来理解多线程和阻塞:
import queueimport threadingimport timedef q_put(q):for i in range(10):q.put('1')print("put 1")for j in range(5):q.put('2')print("put 2")time.sleep(1)def q_get(q):for i in range(15):temp = q.get()q.task_done()print("get", temp)time.sleep(0.5)Q = queue.Queue()# 设置了两个子线程,可以通过调整thread.join()的位置来观察输出结果的不同t1 = threading.Thread(target=q_put, args=(Q,))t1.setDaemon(True) # 设置守护线程,这样主线程执行结束后,会同时杀死子线程(主线程结束不意味着程序结束,如果子线程有死循环,)t2 = threading.Thread(target=q_get, args=(Q,))t2.setDaemon(True)t1.start()#t1.join()t2.start()#print("t2 start")#t1.join()#print("t1 finish")#t2.join()#print("t2 finish")Q.join()print('queue is empty now')
- 关于队列join阻塞:q_put()和q_get()中,只有put和get的执行次数相等时,队列的未完成任务数才会清零,Q.join()才会接触阻塞;否则程序会一直卡在Q.join()这里
- 线程join阻塞:31行的t1.join()的作用是,把所有数据都存入队列后,才会解除阻塞,使得主线程可以继续执行,启动子线程t2
