(一)多线程编程
随着电脑硬件的发展,我们的电脑由最开始的单核CPU到现在的多核CPU,电脑的性能越来越强,我们希望我们的代码尽可能的越快越好,所以我们有时候需要开辟多个线程,让代码并行。
关于线程进程的一些基础知识,可以看我以前的笔记:
【1】https://www.jianshu.com/p/9992a3123870
其他更加详细的帖子:
【2】https://zhuanlan.zhihu.com/p/477826233
-
线程(Thread)是操作系统中能够进行运算的最小单位,他能够被包含在进程之中,是进程的实际运作单位。
-
进程拥有独立的系统资源,该进程下的所有线程共享进程所拥有的计算机资源。线程之间可以并发执行。
多线程的使用分为两步:
- 创建线程
- 开启线程
- 其他操作
线程同步:需要等待使用资源,效率低但安全性高,需要注意死锁问题
线程异步:线程之间互不干扰各执行各的,执行效率高,但是数据容易脏。
(1) threading 模块提供的其他方法:
Python多线程的默认情况(设置线程setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
- threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
- join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。p.join只能join住start开启的进程,而不能join住run开启的进程
- isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
- setDaemon():当setDaemon为true时,主线程结束后,无论子线程1,2是否运行完成,都结束不再往下继续运行。
(2)关于setdaemon和join之间的区别
# 默认情况
import threading
import time
from time import sleep
def fun(a):
for _ in range (10):
print(threading.currentThread())
sleep(a)
# 创建一个线程
thread1 = threading.Thread(target=fun,args=(1,),name="t1") # 10s
thread2 = threading.Thread(target=fun,args=(0.5,),name="t2") # 5s
# 运行线程
thread1.start()
thread2.start()
print("主线程结束",time.strftime("%H:%M:%S"))
# <Thread(t1, started 17164)>
# <Thread(t2, started 15732)>
# 主线程结束 03:14:32
# <Thread(t2, started 15732)>
# <Thread(t1, started 17164)>
# <Thread(t2, started 15732)>
# <Thread(t2, started 15732)>
# <Thread(t1, started 17164)>
# <Thread(t2, started 15732)>
# <Thread(t2, started 15732)>
# <Thread(t1, started 17164)>
# <Thread(t2, started 15732)>
# <Thread(t2, started 15732)>
# <Thread(t1, started 17164)>
# <Thread(t2, started 15732)>
# <Thread(t2, started 15732)>
# <Thread(t1, started 17164)>
# <Thread(t1, started 17164)>
# <Thread(t1, started 17164)>
# <Thread(t1, started 17164)>
# <Thread(t1, started 17164)>
# 主线程不干扰子线程,各运行各的。
# 开启守卫线程
import threading
import time
from time import sleep
def fun(a):
for _ in range (10):
print(threading.currentThread())
sleep(a)
# 创建一个线程
thread1 = threading.Thread(target=fun,args=(1,),name="t1") # 10s
thread2 = threading.Thread(target=fun,args=(0.5,),name="t2") # 5s
# 运行线程
thread1.setDaemon(True)
thread1.start()
thread2.setDaemon(True)
thread2.start()
print("主线程结束",time.strftime("%H:%M:%S"))
# <Thread(t1, started daemon 15056)>
# <Thread(t2, started daemon 16176)>
# 主线程结束 03:10:35
# 主线程一结束,立刻杀死所有子线程
# 设置join,不设置守护线程
import threading
import time
from time import sleep
def fun(a):
for i in range (10):
print(threading.currentThread())
sleep(a)
# 创建一个线程
thread1 = threading.Thread(target=fun,args=(1,),name="t1") # 10s
thread2 = threading.Thread(target=fun,args=(0.5,),name="t2") # 5s
# 运行线程
thread1.start()
thread2.start()
print("等待线程1:",time.strftime("%H:%M:%S"))
thread1.join() # 只要有一个join,主线程就被堵住了,但是不会影响子线程的运行,直到所有join的子线程执行完。
print("等待线程2",time.strftime("%H:%M:%S"))
thread2.join()
print("前面两个子线程运行结束!",time.strftime("%H:%M:%S"))
# <Thread(t1, started 16500)>
# <Thread(t2, started 15664)>
# 等待线程1: 03:01:13
# <Thread(t2, started 15664)>
# <Thread(t1, started 16500)>
# <Thread(t2, started 15664)>
# <Thread(t2, started 15664)>
# <Thread(t1, started 16500)>
# <Thread(t2, started 15664)>
# <Thread(t2, started 15664)>
# <Thread(t1, started 16500)>
# <Thread(t2, started 15664)>
# <Thread(t2, started 15664)>
# <Thread(t1, started 16500)>
# <Thread(t2, started 15664)>
# <Thread(t2, started 15664)>
# <Thread(t1, started 16500)>
# <Thread(t1, started 16500)>
# <Thread(t1, started 16500)>
# <Thread(t1, started 16500)>
# <Thread(t1, started 16500)>
# 等待线程2 03:01:23
# 前面两个子线程运行结束! 03:01:23
# 主线程一遇到join就会被卡住,等待两个线程运行结束,再接着执行主程序,
# 如果主程序执行完还有未执行完的子线程,不会杀死该子线程
(3)自定义线程类
from time import sleep
from threading import Thread
class MyThread(Thread):
def __init__(self,name):
super(MyThread,self).__init__()
self.name = name
def run(self):
for _ in range(5):
print(self.name,"is running!")
sleep(1)
# 创建线程
thread1 = MyThread("t1")
thread2 = MyThread("t2")
# 开启线程
thread1.start()
thread2.start()
print("主线程结束!")
# t1 is running!
# t2 is running!
# 主线程结束!
# t2 is running!
# t1 is running!
# t2 is running!
# t1 is running!
# t2 is running!
# t1 is running!
# t1 is running!
# t2 is running!
(4)lock
在线程中,有一些资源是共享的,例如全局变量。多个线程之间可能会出现同时使用资源的时候。可以给数据加把锁,来防止脏读,等。比较经典的就是生产者,消费者模型,可以去了解一下。
# 不加锁的情况
import threading
import time
from time import sleep
shared_data1 = []
shared_data2 = []
shared_data3 = []
def fun():
"""累加器, 循环1000次"""
global shared_data1
global shared_data2
global shared_data3
for _ in range(1000):
# 模拟读取数据
data1 = shared_data1.copy()
data2 = shared_data2.copy()
data3 = shared_data3.copy()
# 模拟修改数据
data1.extend(["a"]*10)
data2.extend(["a"]*10)
data3.extend(["a"]*10)
# 模拟写入数据
shared_data1 = data1
shared_data2 = data2
shared_data3 = data3
# 创建线程
thread1 =threading.Thread(target=fun,args=(),name="t1")
thread2 =threading.Thread(target=fun,args=(),name="t2")
# 开启线程
thread1.start()
thread2.start()
# 阻塞主进程
thread1.join()
thread2.join()
print(len(shared_data1) == len(shared_data2) == len(shared_data3))# 理论上应当为True结果为False
print(len(shared_data1),len(shared_data2),len(shared_data3))# 理论上应当为20000,结果为17110 17720 17250
# 加上锁以后
import threading
import time
from time import sleep
shared_data1 = []
shared_data2 = []
shared_data3 = []
lock = threading.Lock()
def fun():
"""累加器, 循环10000次"""
global shared_data1
global shared_data2
global shared_data3
global lock
for _ in range(10000):
lock.acquire()
# 模拟读取数据
data1 = shared_data1.copy()
data2 = shared_data2.copy()
data3 = shared_data3.copy()
# 模拟修改数据
data1.extend(["a"]*10)
data2.extend(["a"]*10)
data3.extend(["a"]*10)
# 模拟写入数据
shared_data1 = data1
shared_data2 = data2
shared_data3 = data3
lock.release()
# 创建线程
thread1 =threading.Thread(target=fun,args=(),name="t1")
thread2 =threading.Thread(target=fun,args=(),name="t2")
# 开启线程
thread1.start()
thread2.start()
# 阻塞主进程
thread1.join()
thread2.join()
print(len(shared_data1) == len(shared_data2) == len(shared_data3))# 理论上应当为True,结果为True
print(len(shared_data1),len(shared_data2),len(shared_data3))# 理论上应当为200000,结果为200000 200000 200000
网友评论