python 线程详解
Posted 楊木木8023
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 线程详解相关的知识,希望对你有一定的参考价值。
1、什么是线程?
线程:操作系统提供的抽象概念,是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。
2、python如何实现多线程?
python通常使用threading模块实现多线程,导入threading包,然后对象名= threading.Thread(target = 函数名) 创建线程对象,最后对象名.start() 创建线程。主要的线程方法:run(): 用以表示线程活动的方法;start():启动线程活动;join([time]): 等待至线程中止。
https://docs.python.org/zh-cn/3.8/library/threading.html?highlight=threading#module-threading
3、实现多线程?
import threading
import time
def task1(s):
task_list = ['file1', 'file2', 'file3']
for i in task_list:
print('正在下载:'.format(i))
time.sleep(s)
print('下载成功:'.format(i))
def task2(s):
task_list = ['music1', 'music2', 'music3', 'music4']
for i in task_list:
print('正在听音乐:'.format(i))
time.sleep(s)
if __name__ == '__main__':
t1 = threading.Thread(target=task1, args=(1,)) # 创建线程1
t2 = threading.Thread(target=task2, args=(0.5,)) # 创建线程2
t1.start() # 启动线程
t2.start()
4、全局解释器锁?
全局解释器锁(GIL)只允许1个Python线程控制Python解释器。这也就意味着同一时间点只能有1个Python线程运行。如果你的Python程序只有一个线程,那么全局解释器锁可能对你的影响不大,但是如果你的程序是CPU密集型同时使用了多线程,那么程序运行可能会受到很大影响。
Python为了能够支持多线程,而解决多线程之间数据完整性和状态同步的最简单方法就是加锁,所以在python创立之初默认加上了GIL。
具体可以参考:https://zhuanlan.zhihu.com/p/361695757
但是在python当中,当计算强度不高时,GIL默认会加上,但是当计算强度比较大的时候,GIL又会释放,下面两个例子说明。
(1)当计算强度不高时,由于有锁,所以数据并不会出错
import threading
import time
"""
两个线程共享一个全局变量,
每个线程都对变量进行操作,输出线程结束后的变量的值
"""
tacket = 1000
def task1():
global tacket
for i in range(0, 150):
tacket -= 1
def task2():
global tacket
for i in range(0, 160):
tacket -= 1
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
print(tacket)
结果:
690
(2)当计算强度高时,锁被释放,所以数据出错
import threading
import time
"""
两个线程共享一个全局变量,
每个线程都对变量进行操作,输出线程结束后的变量的值
"""
tacket = 10000000
def task1():
global tacket
for i in range(0, 1500000):
tacket -= 1
def task2():
global tacket
for i in range(0, 1600000):
tacket -= 1
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
print(tacket)
结果:
7217607
5、线程同步(加锁)
多线程的可以共享主线程的数据,在操作的时候可能会存在数据不同的问题解决办法是,加锁,锁 是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对 Lock 对象加锁,线程在开始访问共享资源之前应先请求获得 Lock 对象。当对共享资源访问完成后,程序释放对 Lock 对象的锁定。
Python 的 threading 模块引入了锁(Lock)。threading 模块提供了 Lock 和 RLock 两个类,它们都提供了如下两个方法来加锁和释放锁:
- acquire(blocking=True, timeout=-1):请求对 Lock 或 RLock 加锁,其中 timeout 参数指定加锁多少秒。
- release():释放锁。
Lock 和 RLock 的区别如下:
- threading.Lock:它是一个基本的锁对象,每次只能锁定一次,其余的锁请求,需等待锁释放后才能获取。
- threading.RLock:它代表可重入锁(Reentrant Lock)。对于可重入锁,在同一个线程中可以对它进行多次锁定,也可以多次释放。如果使用 RLock,那么 acquire() 和 release() 方法必须成对出现。如果调用了 n 次 acquire() 加锁,则必须调用 n 次 release() 才能释放锁。
对线程进行加锁之后,计算结果就不会出错了
import threading
import time
"""
两个线程共享一个全局变量,
每个线程都对变量进行操作,输出线程结束后的变量的值
"""
lock = threading.Lock()
tacket = 10000000
def task1():
global tacket
lock.acquire()
for i in range(0, 1500000):
tacket -= 1
lock.release()
def task2():
global tacket
lock.acquire()
for i in range(0, 1600000):
tacket -= 1
lock.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
print(tacket)
结果:
6900000
6、死锁
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源时,就会造成死锁。尽管死锁很少发生,但一旦发生就会造成应用的停止响应。
造成死锁的代码:
"""
开发过程中使用线程,在线程间共享多个资源的时候,
如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
尽管死锁很少发生,但一旦发生就会造成应用的停止响应,程序不做任何事情。
"""
import threading
import time
lockA = threading.Lock()
lockB = threading.Lock()
def task1():
if lockA.acquire():
print('获取了A锁') # 如果可以获取到锁则返回True
time.sleep(0.5)
if lockB.acquire(): # 如果锁被占用,等待
print('又获取了B锁')
lockB.release()
lockA.release()
def task2():
if lockB.acquire():
print('获取了B锁')
time.sleep(0.5)
if lockA.acquire():
print('又获取了A锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
利用超时时间避免死锁:
import threading
import time
lockA = threading.Lock()
lockB = threading.Lock()
def task1():
if lockA.acquire(timeout=5):
print('获取了A锁') # 如果可以获取到锁则返回True
time.sleep(0.5)
if lockB.acquire(timeout=5): # 如果锁被占用,等待
print('又获取了B锁')
lockB.release()
lockA.release()
def task2():
if lockB.acquire(timeout=5):
print('获取了B锁')
time.sleep(0.5)
if lockA.acquire(timeout=5):
print('又获取了A锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
以上是关于python 线程详解的主要内容,如果未能解决你的问题,请参考以下文章