基本知识:
- 一、什么是多线程Threading
- 二、添加线程Thread
- 三、join功能
- 四、存储进程结果Queue
- 五、GIL不一定有效率
- 六、线程锁 Lock
一、什么是多线程Threading
二、添加线程Thread
import threading # 导入模块print(threading.active_count()) # 获取激活的线程数
print(threading.enumerate()) # 查看所有线程信息
print(threading.current_thread()) # 查看正在运行的线程def thread_job():print("This is thread of %s",threading.current_thread())if __name__ == '__main__':print("This is thread of %s",threading.current_thread())thread = threading.Thread(target=thread_job,) # 定义线程:注意target只写函数名,不能带括号thread.start() # 让线程开始工作
结果:
1
[<_MainThread(MainThread, started 4661450176)>]
<_MainThread(MainThread, started 4661450176)>
This is thread of %s <_MainThread(MainThread, started 4661450176)>
This is thread of %s <Thread(Thread-1, started 123145514528768)>
三、join功能
1. 不使用join()的结果:主线程Main thread 和子线程 Thread1 同时进行
import threading
import time def thread_job():print("Thread1 start")time.sleep(1) # 模拟任务消耗时间print("Thread1 finish")if __name__ == '__main__':thread = threading.Thread(target=thread_job,name="Thread1") thread.start() print("All finish")
预期结果:
Thread1 start
Thread1 finish
All finish
实际结果:
Thread1 start
All finish
Thread1 finish
2. 使用join()的结果:在子线程 Thread1 完成之后再执行主线程Main thread
if __name__ == '__main__':thread = threading.Thread(target=thread_job,name="Thread1") thread.start() thread.join()print("All finish")
结果:
Thread1 start
Thread1 finish
All finish
3. 如果是多个线程,为了规避不必要的麻烦,推荐1221的V型排布
thread_1.start()
thread_2.start()
thread_2.join()
thread_1.join()
print("All finish")
四、存储进程结果Queue
import threading
import time
from queue import Queue def thread_job(l,q): # l:列表,q:队列,功能:计算列表每个元素的平凡,结果保存在队列中for i in range(len(l)):l[i] = l[i]**2q.put(l) # 多线程调用的函数不能用return返回值,故用队列保存结果if __name__ == '__main__':q = Queue()data = [[1,2,3],[4,5,6],[7,8,9]]threads = []for i in range(3): # 定义三个线程t = threading.Thread(target=thread_job,args=(data[i],q))t.start() # 开启线程threads.append(t) # 把每个线程加入到线程列表中for thread in threads: # 将3个线程join到主线程中thread.join()for _ in range(3):print(q.get()) # 使用q.get()按顺序从q中取出结果
结果:
[1, 4, 9]
[16, 25, 36]
[49, 64, 81]
五、GIL不一定有效率
python 多线程有时候并不理想,主要原因是python的设计上有一个必要环节Global Interpreter Lock(GIL)全局锁。使得python还是一次性只能处理一个东西。
尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。
实际上,解释器被一个全局解释器锁保护着,它确保任何时候都只有一个Python线程执行。
GIL最大的问题就是Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)。
在讨论普通的GIL之前,有一点要强调的是GIL只会影响到那些严重依赖CPU的程序(比如计算型的)
。
如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适
,因为它们大部分时间都在等待。
实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。
import threading
import timedef job(): # 任务函数for _ in range(10):time.sleep(0.0001)def multiThreading(): # 使用 N 个线程执行任务threads = []for i in range(10):t = threading.Thread(target=job,)t.start()threads.append(t)[t.join() for t in threads]def normal(): # 依次执行任务 N 次for i in range(10):job()if __name__ == '__main__': # 多线程执行和普通顺序调用消耗时间对比nor_start_time = time.time()normal()print("normal_time:",time.time()-nor_start_time)mul_start_time = time.time()multiThreading()print("multiThreading_time:",time.time()-mul_start_time)
当任务执行10次时普通顺序调用和多线程消耗时间:
normal_time: 0.014399051666259766
multiThreading_time: 0.00513005256652832
按理来说,多线程应该比普通方法速度快10倍,因为建立了10个线程,但是结果并没有。这就是其中的GIL在作怪。
六、线程锁 Lock
1. 不使用 Lock 的情况
在一个线程执行运算修改共享内存的过程中,其他线程也可以访问该共享内存,会造成共享内存数据的混乱。
import threadingdef job1():global Afor i in range(10):A += 1print("Job1:",A)def job2():global Afor i in range(10):A += 10print("Job2:",A)if __name__=="__main__":A = 0t1 = threading.Thread(target=job1)t2 = threading.Thread(target=job2)t1.start()t2.start()t2.join()t1.join()
结果:
Job1: 1
Job1: 2
Job1: 13
Job2: 12
Job1: 14
Job2: 24
Job1: 25
Job2: 35
Job1: 36
Job2: 46
Job1: 47
Job2: 57
Job1: 58
Job2: 68
Job1: 69
Job2: 79
Job1: 80
Job2: 90
Job2: 100
Job2: 110
2. 使用 Lock 的情况
Lock 在不同线程使用同一共享内存时,能够确保线程之间互不影响
方法:在每个线程执行运算修改共享内存之前,执行 lock.acquare() 将共享内存上锁,确保当前线程访问时,共享内存不会被其他线程访问,执行运算完毕后,使用 lock.release() 将锁打开,保证其他线程可以使用该共享内存。
import threadingdef job1():global A,locklock.acquire() # 上锁for i in range(10):A += 1print("Job1:",A)lock.release() # 开锁def job2():global A,locklock.acquire() # 上锁for i in range(10):A += 10print("Job2:",A)lock.release() # 开锁if __name__=="__main__":A = 0lock = threading.Lock()t1 = threading.Thread(target=job1)t2 = threading.Thread(target=job2)t1.start()t2.start()t2.join()t1.join()
结果:
Job1: 1
Job1: 2
Job1: 3
Job1: 4
Job1: 5
Job1: 6
Job1: 7
Job1: 8
Job1: 9
Job1: 10
Job2: 20
Job2: 30
Job2: 40
Job2: 50
Job2: 60
Job2: 70
Job2: 80
Job2: 90
Job2: 100
Job2: 110
学习视频连接:?莫烦PYTHON