多线程
注: 本文章仅为学习笔记
完整视频讲解
- 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
- 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
- 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),某进程内的线程再其他进程不可见;
- 调度和切换:线程上下文切换比进程上下文切换要快得多
创建多线程
不使用多线程的时候,此处代码运行,需要6秒程序才能结束
def test(x):
print(x)
time.sleep(3) #设置隔两秒打印一个x
start_time = time.time()
test(1)
test(2)
print(f"共运行了{time.time()-start_time}秒")
运行结果:
1
2
共运行了6.002071380615234秒
创建多线程方法一:用函数创建线程
调用threading库的Thread类
python3中,提供了一个内置模块threading.Thread,可以很方便的创建多线程,threading.Thread()一般接收2个参数:
线程函数名:要放置线程让其后台执行的函数,由用户自己定义,如:target=test 注意不要加()
线程函数的参数: 线程函数名所需的参数,以tuple形式传入,如果不需要参数,可以不指定。
#自定义线程函数
def test(x):
print(x)
time.sleep(3) #设置隔两秒打印一个x
#创建线程t1,指定参数为1
t1 = threading.Thread(target=test,args=(1,)) #target设置执行目标 *args元组,**kwargs字典
#创建线程t2,指定参数为2
t2 = threading.Thread(target=test,args=(2,))
start_time = time.time()
#启动线程t1
t1.start()
#启动线程t2
t2.start()
print(f"共运行了{time.time()-start_time}秒")
运行结果:
1
2
共运行了0.0009975433349609375秒
创建多线程方法二:用类创建线程
自定义一个类,该类必须继承threading.Thread这个父类,必须重写run()这个方法。
这个run()方法相当于第一种方法中的线程函数,可以写自己需要的业务逻辑代码,在start()后将会调用。
def test(x):
print(x)
time.sleep(3) #设置隔两秒打印一个x
#用类创建线程,继承threading.Thread
class MyThread(threading.Thread):
def __init__(self,n):
super(MyThread,self).__init__()
self.n = n
#重写run方法
def run(self):
print("以类的方式创建多线程",self.n)
r1 = MyThread(11)
r2 = MyThread(22)
start_time = time.time()
r1.start()
r2.start()
print(f"共运行了{time.time()-start_time}秒")
运行结果:
以类的方式创建多线程 11
以类的方式创建多线程 22
共运行了0.0012888908386230469秒
线程锁
为什么需要线程锁?
多个线程对同一个数据进行修改时, 可能会出现不可预料的情况.
如何实现线程锁?
- 实例化一个锁对象;
lock = threading.Lock() - 操作变量之前进行加锁
lock.acquire() - 操作变量之后进行解锁
lock.release()
def run():
global x #设置全体变量
lock.acquire() #操作变量之前进行加锁
x += 1
lock.release() #操作变量之后进行解锁
if __name__ == '__main__':
x = 0 #定义变量x
res = [] #定义列表
lock = threading.Lock() #实例化一个锁对象
#创建多线程
for i in range(100):
t = threading.Thread(target=run)
t.start()
res.append(t) #写入列表
for t in res:
t.join()
print(x)
递归锁
假设创建一把锁,线程会卡死,为死锁
"""递归锁"""
import threading
def run1():
global x
lock.acquire() # 操作变量之前进行加锁
x += 1
lock.release() # 操作变量之后进行解锁
return x
def run2():
global y
lock.acquire() # 操作变量之前进行加锁
y += 1
lock.release() # 操作变量之后进行解锁
return y
def run3():
lock.acquire() # 操作变量之前进行加锁
res1 = run1()
res2 = run2()
lock.release() # 操作变量之后进行解锁
print(res1,res2)
if __name__ == '__main__':
x = 0
y = 0
lock = threading.RLock() #实例化一个锁对象
for i in range(50):
t = threading.Thread(target=run3)
t.start()
while threading.active_count() != 1:
print(f'正在运行{threading.active_count()}个线程')
print('线程运行结束!')
运行结果:
此时线程会卡死,为死锁
解决方法:
使用递归锁threading.RLock()
递归锁:RLcok类的用法和Lock类一样,支持嵌套,在多个锁没有释放的时候一般会使用RLock类