多线程

直接创建Thread对象

  1. import threading
  2. import time
  3. def test():
  4. for i in range(5):
  5. print(threading.current_thread().name+' test ',i)
  6. time.sleep(1)
  7. thread = threading.Thread(target=test) # 比较常用的入参还有name, args(a,), kwargs{key:val,}
  8. thread.start()

自定义类继承Thread并重写方法run

  1. import threading
  2. import time
  3. class TestThread(threading.Thread):
  4. def __init__(self,name=None):
  5. threading.Thread.__init__(self,name=name)
  6. def run(self):
  7. for i in range(5):
  8. print(threading.current_thread().name + ' test ', i)
  9. time.sleep(1)
  10. thread = TestThread(name='TestThread')
  11. thread.start()

阻塞

thread.join():添加阻塞(阻塞的是主线程,与其他子线程无关),只有在当前子线程的任务全部结束后,才会解除阻塞。
参考:https://www.cnblogs.com/cnkai/p/7504980.html

多线程的常见问题:
1.多线程的创建应该使用两个循环分别处理start和join函数,即可实现并发,并阻塞主线程;知道所有子线程结束后,再解除阻塞
参考:https://blog.csdn.net/brucewong0516/article/details/81050792

  1. threads = [Thread() for i in range(5)]
  2. for thread in threads:
  3. thread.start()
  4. for thread in threads:
  5. thread.join()
  1. 主线程结束不意味着程序结束,之后所有线程全部结束才行

如果子线程存在死循环,即使主线程结束了,子线程也会一直运行,导致程序一直运行,无法退出;通过设置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是线程安全的

可以通过运行下面这个程序来理解多线程和阻塞:

  1. import queue
  2. import threading
  3. import time
  4. def q_put(q):
  5. for i in range(10):
  6. q.put('1')
  7. print("put 1")
  8. for j in range(5):
  9. q.put('2')
  10. print("put 2")
  11. time.sleep(1)
  12. def q_get(q):
  13. for i in range(15):
  14. temp = q.get()
  15. q.task_done()
  16. print("get", temp)
  17. time.sleep(0.5)
  18. Q = queue.Queue()
  19. # 设置了两个子线程,可以通过调整thread.join()的位置来观察输出结果的不同
  20. t1 = threading.Thread(target=q_put, args=(Q,))
  21. t1.setDaemon(True) # 设置守护线程,这样主线程执行结束后,会同时杀死子线程(主线程结束不意味着程序结束,如果子线程有死循环,)
  22. t2 = threading.Thread(target=q_get, args=(Q,))
  23. t2.setDaemon(True)
  24. t1.start()
  25. #t1.join()
  26. t2.start()
  27. #print("t2 start")
  28. #t1.join()
  29. #print("t1 finish")
  30. #t2.join()
  31. #print("t2 finish")
  32. Q.join()
  33. print('queue is empty now')
  1. 关于队列join阻塞:q_put()和q_get()中,只有put和get的执行次数相等时,队列的未完成任务数才会清零,Q.join()才会接触阻塞;否则程序会一直卡在Q.join()这里
  2. 线程join阻塞:31行的t1.join()的作用是,把所有数据都存入队列后,才会解除阻塞,使得主线程可以继续执行,启动子线程t2