Python
运算调度的最小单位
线程
- 伪线程
- Python没有做到真正实现线程的极致性能(受GIL锁的影响)
- 线程特点
- 共享全局变量
- 应用场景
- 运算率小的时候(共享数据的耗时操作,下载、爬虫、文件I/O等)
- GIL锁
- 全局解释器锁(线程同步),优点:防止数据不安全,缺点:效率低
- 原理是保证每个线程的任务做完时才让另一个线程接手(而不是做到一半阻塞了让另一个线程先做,导致从阻塞状态到运行状态时共享的数据变了)
- 底层自动解开GIL锁的时机是达到一定运算率时(手动加锁不会自动解开)
代码示例:
import threading # 导入线程类
from time import sleep
def foo(args):
list01 = ['任务01', '任务02', '任务03']
for i in list01:
print('正在下载:{}'.format(i))
sleep(args) # 模拟阻塞状态
print('下载 {} 成功!'.format(i))
if __name__ == '__main__':
P01 = threading.Thread(target=foo, name='线程01', args=(1,)) # 创建线程对象
P02 = threading.Thread(target=foo, name='线程02', args=(2,))
P01.start() # 启动线程
P02.start()
'''
正在下载:任务01
正在下载:任务01
下载 任务01 成功!
正在下载:任务02
下载 任务01 成功!
正在下载:任务02
下载 任务02 成功!
正在下载:任务03
下载 任务03 成功!
下载 任务02 成功!
正在下载:任务03
下载 任务03 成功!
'''
多线程同步
代码示例:
import threading
import time
lock = threading.Lock() # 获取锁
list01 = [0] * 2 # [0,0]
def foo():
lock.acquire() # 给任务加锁(可加上timeout参数)
for i in range(len(list01)):
list01[i] = 1
time.sleep(0.3)
lock.release() # 给任务解锁
def bar():
lock.acquire()
for i in range(len(list01)):
print(list01[i])
time.sleep(0.3)
lock.release()
if __name__ == '__main__':
P01 = threading.Thread(target=foo)
P02 = threading.Thread(target=bar)
P01.start()
P02.start()
P01.join()
P02.join()
print(list01)
'''
1
1
1
1
1
1
1
1
1
1
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
'''
死锁
- 概念
- 如果两个线程分别占有一部分资源且同时等待对方占有的资源时,就会造成死锁
- 理解
- 有两把锁(A,B)与两个线程(P01,P02)
- P01拥有A锁,P01在执行的过程中阻塞了,执行权让给了P02
- 而P02又拥有B锁,P02在执行的过程中也阻塞了,执行权让给了P01
- P01在执行的过程中又想要B锁,但是B锁被P02占有了,只能无限等待P02释放锁(因为加锁本身是阻塞函数)
- 解决办法
- 给acquire函数加个实参timeout(返回值:False)
代码示例:
from threading import Thread, Lock
from time import sleep
lock01 = Lock()
lock02 = Lock()
class Foo(Thread):
def run(self):
if lock01.acquire(): # 返回值:bool
print(self.name + '获取了A锁')
sleep(0.3)
if lock02.acquire():
print(self.name + '又获取了B锁,原来还有A锁')
lock02.release()
lock01.release()
class Bar(Thread):
def run(self):
if lock02.acquire():
print(self.name + '获取了B锁')
sleep(0.3)
if lock01.acquire():
print(self.name + '又获取了A锁,原来还有B锁')
lock01.release()
lock02.release()
if __name__ == '__main__':
P01 = Foo()
P02 = Bar()
P01.start()
P02.start()
'''
Thread-1获取了A锁
Thread-2获取了B锁
'''
线程间通信
代码示例:
import threading
import time
import random
import queue
def foo(args):
for i in range(10):
num = random.randint(1, 100)
print(num)
args.put(num)
time.sleep(0.3)
else:
args.put(None)
def bar(args):
while True:
item = args.get()
if item is None:
break
print('收到数据:{}'.format(item))
time.sleep(0.3)
if __name__ == '__main__':
P01 = queue.Queue(5)
P02 = threading.Thread(target=foo, args=(P01,))
P03 = threading.Thread(target=bar, args=(P01,))
P02.start()
P03.start()
P02.join()
P03.join()
'''
28
收到数据:28
51
收到数据:51
33
收到数据:33
53
收到数据:53
48
收到数据:48
81
收到数据:81
86
收到数据:86
1
收到数据:1
70
收到数据:70
67
收到数据:67
'''