Python 多线程(Threading)
仅供个人学习
来源于 莫凡Python:https://mofanpy.com/tutorials/python-basic/basic/ 侵删
什么是多线程 Threading
多线程是加速程序计算的有效方式,使用 threading 模块
添加线程 Thread
import threading
# 获取已激活的线程数
threading.active_count()
>>> 2
# 查看所有线程信息
threading.enumerate()
>>> [<_MainThread(MainThread, started 140736011932608)>, <Thread(SockThread, started daemon 123145376751616)>]
# 查看正在运行的线程
threading.current_thread()
# 添加线程 threading.Thread()接收参数target代表这个线程要完成的任务,需自行定义
def thread_job():
print('This is a thread of %s' % threading.current_thread())
def main():
thread = threading.Thread(target=thread_job,) # 定义线程
thread.start() # 让线程开始工作
if __name__ == '__main__':
main()
join 功能
不加
join
完全取决于两个线程的执行速度。加入join
对线程进行控制。例:
t1.join()
等待t1线程结束后,在往下执行。
存储进程结果 Queue
# 导入线程、队列的标准模块
import threading
import time
from queue import Queue
# 定义一个被多线程调用的函数
def job(l, q):
for i in range(len(l)):
l[i] = l[i] ** 2
q.put(l) # 多线程调用的函数不能用return返回值
def multithreading():
q =Queue() #q中存放返回值,代替return的返回值
threads = []
data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]]
for i in range(4): #定义四个线程
t = threading.Thread(target=job,args=(data[i],q)) # Thread首字母要大写,被调用的job函数没有括号,只是一个索引,参数在后面
t.start()#开始线程
threads.append(t) #把每个线程append到线程列表中
for thread in threads:
thread.join()
results = []
for _ in range(4):
results.append(q.get()) #q.get()按顺序从q中拿出一个值
print(results)
if __name__=='__main__':
multithreading()
# 结果
>>> [[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]
GIL 不一定有效率
python 的多线程 threading 有时候并不是特别理想. 最主要的原因是就是, Python 的设计上, 有一个必要的环节, 就是 Global Interpreter Lock (GIL). 这个东西让 Python 还是一次性只能处理一个东西.
GIL:
尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。 实际上,解释器被一个全局解释器锁保护着,它确保任何时候都只有一个Python线程执行。 GIL最大的问题就是Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)。 在讨论普通的GIL之前,有一点要强调的是GIL只会影响到那些严重依赖CPU的程序(比如计算型的)。 如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适, 因为它们大部分时间都在等待。实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力。
线程锁 Lock
lock在不同线程使用同一共享内存时,能够确保线程之间互不影响,使用lock的方法是, 在每个线程执行运算修改共享内存之前,执行
lock.acquire()
将共享内存上锁, 确保当前线程执行时,内存不会被其他线程访问,执行运算完毕后,使用lock.release()
将锁打开, 保证其他的线程可以使用该共享内存。
# 未加锁输出结果
"""
job1job2 11
job2 21
job2 31
job2 41
job2 51
job2 61
job2 71
job2 81
job2 91
job2 101
1
job1 102
job1 103
job1 104
job1 105
job1 106
job1 107
job1 108
job1 109
job1 110
"""
# 加锁代码
import threading
def job1():
global A,lock
lock.acquire()
for i in range(10):
A+=1
print('job1',A)
lock.release()
def job2():
global A,lock
lock.acquire()
for i in range(10):
A+=10
print('job2',A)
lock.release()
if __name__== '__main__':
lock=threading.Lock()
A=0
t1=threading.Thread(target=job1)
t2=threading.Thread(target=job2)
t1.start()
t2.start()
t1.join()
t2.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
"""