Python基础 - 11进程与线程
并发: 有多个线程在操作时,如果系统只有一个CPU, 只能把CPU运行时间划分成若干个时间片,线程抢占时间片。
并行: 当系统有多个CPU时,线程操作有可能非并发,一个CPU执行一个线程,线程互不抢占。
实现多任务的方式: 1. 多进程模式 2. 多线程模式 3. 协程
进程: 计算密集型 线程: 耗时操作,爬虫 ,IO操作
一、进程
def task1():
while True:
sleep(1)
# print('--> 任务1...')
print('--> 任务1...',os.getpid(),' -- ',os.getppid())
def task2():
while True:
sleep(2)
# print('--> 任务2...')
print('--> 任务2...',os.getpid(),' -- ',os.getppid())
# 只打印任务1
if __name__ == '__main__':
task1()
task2()
1.1 进程创建: 在Linux下使用fork函数创建进程, 在Windows系统上引用multiprocessing模块创建进程。
process.start(): 启动进程并执行任务
process.run(): 只是执行了任务但是没有启动进程
from multiprocessing import Process if __name__ == '__main__': p1 = Process(target=task1, name='Task 1') p1.start() print(p1.name) p2 = Process(target=task2, name='Task 2') p2.start() print(p2.name) ''' --> 任务1... 4792 -- 2268 --> 任务2... 3592 -- 2268 --> 任务1... 4792 -- 2268 --> 任务1... 4792 -- 2268 --> 任务2... 3592 -- 2268 --> 任务1... 4792 -- 2268 '''
1.2 进程终止
def task1(s,name):
while True:
sleep(s)
print('--> 任务1...',os.getpid(),' -- ',os.getppid(), name)
def task2(s, name):
while True:
sleep(s)
print('--> 任务2...',os.getpid(),' -- ',os.getppid(),name)
if __name__ == '__main__':
p1 = Process(target=task1, name='Task 1', args=(1,'讲师'))
p1.start()
print(p1.name)
p2 = Process(target=task2, name='Task 2', args=(2,'学生'))
p2.start()
print(p2.name)
number = 0
while True:
number += 1
sleep(0.2)
if number == 20:
p1.terminate()
p2.terminate()
break
print('-----------')
'''
Task 1
Task 2
-----------
--> 任务1... 544 -- 6800 讲师
--> 任务1... 544 -- 6800 讲师
--> 任务2... 5160 -- 6800 学生
'''
1.3 进程使用全局变量
m = 1
list_m = []
def task1(s, name):
global m
while True:
sleep(s)
m += 1
list_m.append(str(m)+'task1')
print('--> 任务1...,m值为 ', m, list_m)
def task2(s, name):
global m
while True:
sleep(s)
m += 1
list_m.append(str(m) + 'task2')
print('--> 任务2...,m值为 ', m, list_m)
if __name__ == '__main__':
p1 = Process(target=task1, name='Task 1', args=(1, '讲师'))
p1.start()
p2 = Process(target=task2, name='Task 2', args=(2, '学生'))
p2.start()
while True:
sleep(1)
m += 1
print('--> 任务main...,m值为 ', m)
'''
--> 任务main...,m值为 2
--> 任务1...,m值为 2 ['2task1']
--> 任务main...,m值为 3
--> 任务1...,m值为 3 ['2task1', '3task1']
--> 任务2...,m值为 2 ['2task2']
--> 任务main...,m值为 4
--> 任务1...,m值为 4 ['2task1', '3task1', '4task1']
--> 任务main...,m值为 5
--> 任务1...,m值为 5 ['2task1', '3task1', '4task1', '5task1']
--> 任务2...,m值为 3 ['2task2', '3task2']
--> 任务main...,m值为 6
--> 任务1...,m值为 6 ['2task1', '3task1', '4task1', '5task1', '6task1']
--> 任务main...,m值为 7
--> 任务1...,m值为 7 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1']
--> 任务2...,m值为 4 ['2task2', '3task2', '4task2']
--> 任务main...,m值为 8
--> 任务1...,m值为 8 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1']
--> 任务main...,m值为 9
--> 任务1...,m值为 9 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1']
--> 任务2...,m值为 5 ['2task2', '3task2', '4task2', '5task2']
'''
1.4 自定义进程
# 进程: 自定义, 继承Process,重写run()方法
class MyProcess(Process):
def __init__(self, name):
# 没有super,报错 AttributeError: 'MyProcess' object has no attribute '_closed'
super(MyProcess, self).__init__()
self.name = name
def run(self):
n = 1
while True:
print('{} --> 自定义进程,n:{}:'.format(self.name, n))
sleep(1)
n += 1
if __name__ == '__main__':
p = MyProcess('小明')
p.start()
'''
小明 --> 自定义进程,n:1:
小明 --> 自定义进程,n:2:
小明 --> 自定义进程,n:3:
小明 --> 自定义进程,n:4:
小明 --> 自定义进程,n:5:
......
'''
1.5 进程池: 非阻塞式
from multiprocessing import Pool
from random import random
import time
# 非阻塞式进程: 全部添加到队列中,任务结束后,立刻返回,并没有等待其他的进程完毕。
def task(task_name):
print('开始工作了,{},当前时间为{}'.format(task_name, time.time()))
start = time.time()
time.sleep(random() * 2)
end = time.time()
print('完成工作了,{},当前时间为{},用时为{}秒, 进程ID为{}'.format(task_name, time.time(), (end - start), os.getpid()))
if __name__ == '__main__':
pool = Pool(5)
tasks_name = ['打篮球','写文档','听歌','看书', '打游戏', '打豆豆','豆豆哭啦']
for task_name in tasks_name:
pool.apply_async(task, args=(task_name,))
pool.close()
pool.join() # 让主进程让步, 否则程序直接结束
print('主进程结束啦')
'''
开始工作了,打篮球,当前时间为1633141511.4560132
开始工作了,写文档,当前时间为1633141511.4620287
开始工作了,听歌,当前时间为1633141511.4660397
开始工作了,看书,当前时间为1633141511.4715543
开始工作了,打游戏,当前时间为1633141511.4795759
完成工作了,听歌,当前时间为1633141512.0300672,用时为0.5640275478363037秒, 进程ID为824
开始工作了,打豆豆,当前时间为1633141512.0305643
完成工作了,打篮球,当前时间为1633141512.3323722,用时为0.8763589859008789秒, 进程ID为4820
开始工作了,豆豆哭啦,当前时间为1633141512.3328476
完成工作了,打豆豆,当前时间为1633141512.4075725,用时为0.37700819969177246秒, 进程ID为824
完成工作了,打游戏,当前时间为1633141512.862915,用时为1.3833391666412354秒, 进程ID为6896
完成工作了,豆豆哭啦,当前时间为1633141512.9780905,用时为0.6452429294586182秒, 进程ID为4820
完成工作了,写文档,当前时间为1633141513.0192263,用时为1.5571975708007812秒, 进程ID为6008
完成工作了,看书,当前时间为1633141513.0940337,用时为1.6224794387817383秒, 进程ID为2640
主进程结束啦
'''
非阻塞进程: 带回调函数
container = [] def callback_func(n): container.append(n) if __name__ == '__main__': pool = Pool(5) tasks_name = ['打篮球','写文档','听歌','看书', '打游戏', '打豆豆','豆豆哭啦'] for task_name in tasks_name: pool.apply_async(task, args=(task_name,), callback=callback_func(task_name))
1.6 进程池: 阻塞式进程
from multiprocessing import Pool
from random import random
import time
# 阻塞式进程: 添加一个任务,执行一个任务;如果一个任务不结束另一个任务进不来
def task(task_name):
print('开始工作了,{},当前时间为{}'.format(task_name, time.time()))
start = time.time()
time.sleep(random() * 2)
end = time.time()
print('完成工作了,{},当前时间为{},用时为{}秒, 进程ID为{}'.format(task_name, time.time(), (end - start), os.getpid()))
if __name__ == '__main__':
pool = Pool(5)
tasks_name = ['打篮球','写文档','听歌','看书', '打游戏', '打豆豆','豆豆哭啦']
for task_name in tasks_name:
pool.apply(task, args=(task_name,))
pool.close()
pool.join()
print('主进程结束啦')
'''
开始工作了,打篮球,当前时间为1633141835.9163513
完成工作了,打篮球,当前时间为1633141836.2893722,用时为0.37302088737487793秒, 进程ID为1644
开始工作了,写文档,当前时间为1633141836.290854
完成工作了,写文档,当前时间为1633141836.7435563,用时为0.45217323303222656秒, 进程ID为4252
开始工作了,听歌,当前时间为1633141836.745066
完成工作了,听歌,当前时间为1633141838.388317,用时为1.6432511806488037秒, 进程ID为5384
开始工作了,看书,当前时间为1633141838.3892856
完成工作了,看书,当前时间为1633141839.1293015,用时为0.740015983581543秒, 进程ID为2324
开始工作了,打游戏,当前时间为1633141839.1298025
完成工作了,打游戏,当前时间为1633141839.7122407,用时为0.5824382305145264秒, 进程ID为4864
开始工作了,打豆豆,当前时间为1633141839.7135873
完成工作了,打豆豆,当前时间为1633141839.8288903,用时为0.11530303955078125秒, 进程ID为1644
开始工作了,豆豆哭啦,当前时间为1633141839.8299038
完成工作了,豆豆哭啦,当前时间为1633141840.6465697,用时为0.8161656856536865秒, 进程ID为4252
主进程结束啦
'''
1.7 进程间通信
q.put() / q.get() / q.qsize() / q.empty() / q.full()
from multiprocessing import Queue
q = Queue(5)
q.put('A')
q.put('B')
q.put('C')
q.put('D')
q.put('E')
print(q.qsize())
q.put('F') # put() 如果queue 满了,则只能等待, 除非有空 则添加成功
print(q.get(timeout=2))
print(q.get(timeout=2))
if q.full():
q.put('F', timeout=3)
else:
print('超时')
'''
5
A
B
超时
'''
q.put_nowait('G')
print(q.get_nowait()) # C
from multiprocessing import Process, Queue
from time import sleep
def download(q):
images = ['abc.jpg','boy.png','girl.jpeg']
for image in images:
print('正在下载:', image)
sleep(0.5)
q.put(image)
def getfile(q):
while True:
try:
file = q.get(timeout=5)
print('{}保存成功!'.format(file))
except:
print('全部保存完毕')
break
if __name__ == '__main__':
q = Queue(5)
p1 = Process(target=download, args=(q,))
p2 = Process(target=getfile, args=(q,))
p1.start()
p1.join()
p2.start()
p2.join()
'''
正在下载: abc.jpg
正在下载: boy.png
正在下载: girl.jpeg
abc.jpg保存成功!
boy.png保存成功!
girl.jpeg保存成功!
全部保存完毕
'''
二、 多线程
2.1 线程创建
import threading
from time import sleep
def download(n):
images = ['abc.jpg','boy.png','girl.jpeg']
for image in images:
print('正在下载:', image)
sleep(n)
print('下载成功: ', image)
t = threading.Thread(target=download, name='t1', args=(1,))
t.start()
def listenMusic():
musics=['ABC','星星','月亮','太阳']
for music in musics:
sleep(0.5)
print('正在听{}'.format(music))
if __name__ == '__main__':
t = threading.Thread(target=download, name='t', args=(1,))
t.start()
# n = 1
# while True:
# print(n)
# n+=1
# sleep(1)
t1 = threading.Thread(target=listenMusic, name='t')
t1.start()
'''
正在下载: abc.jpg
1
2下载成功:
abc.jpg
正在下载: boy.png
下载成功: 3
boy.png
正在下载: girl.jpeg
4
下载成功: girl.jpeg
5
'''
'''
正在下载: abc.jpg
正在听ABC
下载成功: abc.jpg
正在下载: boy.png
正在听星星
正在听月亮
下载成功: boy.png
正在下载: girl.jpeg
正在听太阳
下载成功: girl.jpeg
'''
2.2 线程状态: 新建、 就绪、 运行、 阻塞、 结束
2.3 线程共享变量
import threading
from time import sleep
money = 1000
def run1():
global money
for i in range(100):
sleep(0.1)
money -= 1
print('run1的money值为:',money)
def run2():
global money
for i in range(100):
sleep(0.2)
money -= 1
print('run2的money值为:', money)
if __name__ == '__main__':
th1 = threading.Thread(target=run1, name='th1')
th2 = threading.Thread(target=run2, name='th2')
th3 = threading.Thread(target=run1, name='th3')
th4 = threading.Thread(target=run2, name='th4')
th1.start()
th2.start()
th3.start()
th4.start()
th1.join()
th2.join()
th3.join()
th4.join()
print('money:', money) # money: 600
共享变量值较大时
import threading
n = 0
def task1():
global n
for i in range(10000000):
n += 1
print('task1 的 n 值为: ', n)
def task2():
global n
for i in range(10000000):
n += 1
print('task2 的 n 值为: ', n)
if __name__ == '__main__':
th1 = threading.Thread(target=task1, name='th1')
th2 = threading.Thread(target=task2, name='th2')
th1.start()
th2.start()
th1.join()
th2.join()
print('主线程 的 n 值为:', n)
'''
task1 的 n 值为: 11459227
task2 的 n 值为: 11513727
主线程 的 n 值为: 11513727
'''
2.4 线程锁
GIL 全局解释器锁, python 底层只要用线程,就默认加锁。
import threading, random, time
lock = threading.Lock()
list1 = [0] * 10
def task1():
# 获取线程锁,如有锁,则等待锁释放
lock.acquire()
for i in range(len(list1)):
list1[i] = 1
time.sleep(0.5)
lock.release()
def task2():
# 获取线程锁,如有锁,则等待锁释放
lock.acquire()
for i in range(len(list1)):
print('--->i:',list1[i])
time.sleep(0.5)
lock.release()
if __name__ == '__main__':
th1 = threading.Thread(target=task1, name='th1')
th2 = threading.Thread(target=task2, name='th2')
th2.start()
th1.start()
th2.join()
th1.join()
print('主线程 的 list1 值为:', list1)
'''
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 1
--->i: 0
--->i: 0
--->i: 0
主线程 的 list1 值为: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
'''
'''
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
--->i: 0
主线程 的 list1 值为: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
'''
2.5 死锁
# 死锁
from threading import Thread
lockA = threading.Lock()
lockB = threading.Lock()
class MyThread(Thread):
def __init__(self,name):
super(MyThread, self).__init__()
self.name = name
def run(self):
if lockA.acquire():
print(self.name + '获取了A锁')
time.sleep(0.1)
if lockB.acquire(timeout=2):
print(self.name + '又获取了B锁')
lockB.release()
lockA.release()
class MyThread1(Thread):
def __init__(self,name):
super(MyThread1,self).__init__()
self.name = name
def run(self):
if lockB.acquire():
print(self.name + '获取了B锁')
time.sleep(0.1)
if lockA.acquire():
print(self.name + '又获取了A锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = MyThread('t1')
t2 = MyThread1('t2')
t1.start()
t2.start()
t1.join()
t2.join()
'''
t1获取了A锁
t2获取了B锁 # 程序卡死
'''
'''
t1获取了A锁
t2获取了B锁
t2又获取了A锁 # 程序等待一段时间,结束
'''
2.6 生产者与消费者
python的queue模块提供了同步的、线程安全的队列类, 包括先进先出队列Queue、LifoQueue、PriorityQueue。
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue']
# 生产者与消费者
import threading, queue, random, time
def producer(q):
i = 0
while i < 10:
num = random.randint(1,100)
q.put(num)
print('生产者生成的数据为:%d' % num)
time.sleep(1)
i += 1
q.put(None)
# 完成任务
q.task_done()
def consumer(q):
while True:
item = q.get()
if item is None:
break
print('消费者消费的数据为:%s' % item)
time.sleep(4)
q.task_done()
if __name__ == '__main__':
queue = queue.Queue(5)
th1 = threading.Thread(target=producer, args=(queue,))
th2 = threading.Thread(target=consumer, args=(queue,))
th1.start()
th2.start()
th1.join()
th2.join()
print('over!!!')
'''
生产者生成的数据为:30
消费者消费的数据为:30
生产者生成的数据为:76
生产者生成的数据为:1
生产者生成的数据为:67
消费者消费的数据为:76
生产者生成的数据为:35
生产者生成的数据为:69
生产者生成的数据为:43
消费者消费的数据为:1
生产者生成的数据为:3
......
'''
按顺序打印A0,B0,A1,B1, A2,B2
# yield
def task1():
for i in range(3):
print('A' + str(i))
yield
time.sleep(0.2)
def task2():
for i in range(3):
print('B' + str(i))
yield
time.sleep(0.2)
if __name__ == '__main__':
g1 = task1()
g2 = task2()
while True:
try:
next(g1)
next(g2)
except:
break
'''
A0
B0
A1
B1
A2
B2
'''
三、协程
3.1 greenlet协程
# greenlet 完成协程任务
# (venv) E:\PythonLearn\pythonBase>pip install greenlet
import time
from greenlet import greenlet
def task1():
for i in range(3):
print('A' + str(i))
g2.switch()
time.sleep(0.1)
def task2():
for i in range(3):
print('B' + str(i))
g3.switch()
time.sleep(0.1)
def task3():
for i in range(3):
print('C' + str(i))
g1.switch()
time.sleep(0.1)
if __name__ == '__main__':
g1 = greenlet(task1)
g2 = greenlet(task2)
g3 = greenlet(task3)
g1.switch()
'''
A0
B0
C0
A1
B1
C1
A2
B2
C2
'''
3.2 gevent协程
# (venv) E:\PythonLearn\pythonBase>pip install gevent
import time
import gevent
from gevent import monkey
monkey.patch_all()
def task1():
for i in range(3):
print('A' + str(i))
time.sleep(0.1)
def task2():
for i in range(3):
print('B' + str(i))
time.sleep(0.1)
def task3():
for i in range(3):
print('C' + str(i))
time.sleep(0.1)
if __name__ == '__main__':
g1 = gevent.spawn(task1)
g2 = gevent.spawn(task2)
g3 = gevent.spawn(task3)
g3.join()
g1.join()
g2.join()
print('主进程结束')
'''
A0
A1
A2
B0
B1
B2
C0
C1
C2
主进程结束
'''
'''
A0
B0
C0
A1
B1
C1
A2
B2
C2
主进程结束
'''