多线程
直接创建Thread对象
import threading
import time
def 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 threading
import time
class 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 queue
import threading
import time
def 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