多线程处理可以加速程序,本文介绍Python的多线程模块threading。
1.添加线程
导入模块:
import threading
获取已激活的线程数
threading.active_count()
# 2
查看所有激活的线程信息,输出的结果是一个<_MainThread(...)>
带多个<Thread(...)>
。
threading.enumerate()
# [<_MainThread(MainThread, started 140736011932608)>, <Thread(SockThread, started daemon 123145376751616)>]
查看现在正在运行的线程
threading.current_thread()
# <_MainThread(MainThread, started 140736011932608)>
添加线程,threading.Thread()
接收参数target
代表这个线程要完成的任务,需自行定义
import threading
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()
2.jion()功能
当几个线程同时进行时,耗时短的线程会率先完成。使用join
对控制多个线程的执行顺序非常关键。举个例子,假设我们现在再加一个线程T2
,T2
的任务量较小,会比T1
更快完成:
def T1_job():
print("T1 start\n")
for i in range(10):
time.sleep(0.1)
print("T1 finish\n")
def T2_job():
print("T2 start\n")
print("T2 finish\n")
thread_1 = threading.Thread(target=T1_job, name='T1')
thread_2 = threading.Thread(target=T2_job, name='T2')
thread_1.start() # 开启T1
thread_2.start() # 开启T2
print("all done\n")
输出的”一种”结果是:
T1 start
T2 start
T2 finish
all done
T1 finish
我们试试在T1
启动后,T2
启动前加上thread_1.join()
:
thread_1.start()
thread_1.join() # notice the difference!
thread_2.start()
print("all done\n")
输出结果:
T1 start
T1 finish
T2 start
all done
T2 finish
可以看到,T2
会等待T1
结束后才开始运行。jong()函数功能在于需要完成该线程后才继续执行下面的代码。
我们要想线程1和2同时进行,且全部执行完毕后再运行print“all dong\n”,需要修改在T2
启动后放上thread_1.join()
thread_1.start()
thread_2.start()
thread_1.join() # notice the difference!
print("all done\n")
输出结果:
T1 start
T2 start
T2 finish
T1 finish
all done
3. 需要将多线程处理的结果存储以待使用是,需要用到队列
import threading
import time
from queue import Queue
定义一个多线程函数
在多线程函数中定义一个Queue
,用来保存返回值,代替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到线程列表中
分别join
四个线程到主线程
for thread in threads:
thread.join()
定义一个空的列表results
,将四个线运行后保存在队列中的结果返回给空列表results
results = []
for _ in range(4):
results.append(q.get()) #q.get()按顺序从q中拿出一个值
print(results)
完整的代码
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)
def multithreading():
q =Queue()
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))
t.start()
threads.append(t)
for thread in threads:
thread.join()
results = []
for _ in range(4):
results.append(q.get())
print(results)
if __name___=='__main__':
multithreading()
最后运行结果为:
[[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]
4. 当一个线程需要用到另一个线程处理的结果时,需要等待其处理完毕。此时需要线程锁。
lock在不同线程使用同一共享内存时,能够确保线程之间互不影响,使用lock的方法是, 在每个线程执行运算修改共享内存之前,执行lock.acquire()
将共享内存上锁, 确保当前线程执行时,内存不会被其他线程访问,执行运算完毕后,使用lock.release()
将锁打开, 保证其他的线程可以使用该共享内存。
函数一和函数二加锁
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()
主函数中定义一个Lock
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
5. 多线程不一定会大量节省时间
python 的多线程 threading 有时候并不是特别理想. 最主要的原因是就是, Python 的设计上, 有一个必要的环节, 就是 Global Interpreter Lock (GIL). 这个东西让 Python 还是一次性只能处理一个东西. GIL只会影响到那些严重依赖CPU的程序(比如计算型的)。 如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适, 因为它们大部分时间都在等待。实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。