目录
一进程
单核CPU实现多任务原理:操作系统轮流让各个任务交替执行
多核CPU实现多任务原理:真正的秉性执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU核心数量。所以操作系统也会自动把很多任务轮流调度到每个核心上执行。
在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
进程 > 线程 > 协程
多任务的实现有3种方式:
多进程模式;
多线程模式;
多进程+多线程模式。
多进程
进程(process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
对于操作系统来说,一个任务就是一个进程(Process)
进程创建
Process类说明
Process( target [, name [, args [, kwargs]]])
target:如果传递了函数的引用,可以任务这个子进程就执行这里的代码
args:给target指定的函数传递的参数,以元组的方式传递(可迭代的)
kwargs:给target指定的函数传递命名参数
name:给进程设定一个名字,可以不设定
Process创建的实例对象的常用方法:
start():启动子进程实例(创建子进程)
is_alive():判断进程子进程是否还在活着
join([timeout]):是否等待子进程执行结束,或等待多少秒
terminate():不管任务是否完成,立即终止子进程
Process创建的实例对象的常用属性:
name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
pid:当前进程的pid(进程号)
import os
from multiprocessing import Process
from time import sleep
def task1(s, n):
while True:
sleep(s)
print('这是任务1---------', os.getpid(), '--------', os.getppid(), n)
def task2(s,n):
while True:
sleep(s)
print('这是任务2---------', os.getpid(), '--------', os.getppid(), n)
number = 1
if __name__ == '__main__':
print(os.getpid())
#子进程1
p = Process(target=task1, name='任务1',args=(1,'aa'))#target = 函数名
p.start()
print(p.name)#主进程
#子进程2
p1 = Process(target=task2, name='任务2',args=(2,'bb'))
p1.start()
print(p1.name)#主进程
#主进程
while True:
number += 1
sleep(0.2)
if number == 100:
p.terminate()
p1.terminate()
break
else:
print('---------->number:', number)
print('------------')
print('*************')
输出:
764
任务1
任务2
---------->number: 2
---------->number: 3
---------->number: 4
---------->number: 5
---------->number: 6
这是任务1--------- 21772 -------- 764 aa
---------->number: 7
---------->number: 8
---------->number: 9
---------->number: 10
---------->number: 11
这是任务2--------- 7528 -------- 764 bb
这是任务1--------- 21772 -------- 764 aa
---------->number: 12
---------->number: 13
---------->number: 14
---------->number: 15
---------->number: 16
这是任务1--------- 21772 -------- 764 aa
---------->number: 17
---------->number: 18
---------->number: 19
---------->number: 20
这是任务2--------- 7528 -------- 764 bb
---------->number: 21
这是任务1--------- 21772 -------- 764 aa
---------->number: 22
---------->number: 23
---------->number: 24
---------->number: 25
这是任务1--------- 21772 -------- 764 aa
---------->number: 26
---------->number: 27
---------->number: 28
---------->number: 29
---------->number: 30
这是任务2--------- 7528 -------- 764 bb
这是任务1--------- 21772 -------- 764 aa
……
访问全局变量:
import os
from multiprocessing import Process
from time import sleep
m = 1 #不可变类型
list1 = [] #可变类型
def task1(s, n):
global m
while True:
sleep(s)
m += 1
list1.append(str(m) + 'task1')
print('这是任务1---------', m, list1)
def task2(s,n):
global m
while True:
sleep(s)
m += 1
list1.append(str(m) + 'task2')
print('这是任务2---------', m, list1)
if __name__ == '__main__':
print(os.getpid())
#子进程1
p = Process(target=task1, name='任务1',args=(1,'aa'))
p.start()#子进程1开启
#子进程2
p1 = Process(target=task2, name='任务2',args=(2,'bb'))
p1.start()#子进程2开启
#主进程
while True:
sleep(2)
m += 1
print('--------->main', m)
输出:
这是任务1--------- 2 ['2task1']
--------->main 2
这是任务1--------- 3 ['2task1', '3task1']
这是任务2--------- 2 ['2task2']
这是任务1--------- 4 ['2task1', '3task1', '4task1']
--------->main 3
这是任务2--------- 这是任务1---------3 5 ['2task2', '3task2']['2task1', '3task1', '4task1', '5task1']
这是任务1--------- 6 ['2task1', '3task1', '4task1', '5task1', '6task1']
--------->main 4
这是任务2--------- 4 ['2task2', '3task2', '4task2']
这是任务1--------- 7 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1']
这是任务1--------- 8 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1']
--------->main 5
这是任务2--------- 5 ['2task2', '3task2', '4task2', '5task2']
这是任务1--------- 9 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1']
这是任务1--------- 10 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1']
--------->main 6
这是任务2--------- 6 ['2task2', '3task2', '4task2', '5task2', '6task2']
这是任务1--------- 11 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1']
这是任务1--------- 12 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1']
--------->main 7
这是任务2--------- 7 ['2task2', '3task2', '4task2', '5task2', '6task2', '7task2']
这是任务1--------- 13 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1']
这是任务1--------- 14 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1', '14task1']
--------->main 8
这是任务2--------- 8 ['2task2', '3task2', '4task2', '5task2', '6task2', '7task2', '8task2']
这是任务1--------- 15 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1', '14task1', '15task1']
这是任务1--------- 16 ['2task1', '3task1', '4task1', '5task1', '6task1', '7task1', '8task1', '9task1', '10task1', '11task1', '12task1', '13task1', '14task1', '15task1', '16task1']
等
自定义进程
from multiprocessing import Process
class MyProcess(Process):
def __init__(self, name):
super(MyProcess, self).__init__()
self.name = name
#重写run方法
def run(self):
n = 1
while True:
print('进程名字:'+ self.name)
print('{}--------->自定义进程, n:{}'.format(n, self.name))
n += 1
if __name__ == '__main__':
p = MyProcess('小明')
p.start()
p1 = MyProcess('小红')
p1.start()
进程池之非阻塞式
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是上百个甚至上千个目标,手动的创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。
初始化Pool时,可以指定一个最大的进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行。
#1、非阻塞式进程
'''
特点:
全部添加到队列中,立刻返回,没有等待其他的进程完毕,但是回调函数是等待任务完成之后才调用。
'''
from multiprocessing import Pool
import time
from random import random
import os
#非阻塞式进程
def task(task_name):
print('开始做任务啦!', task_name)
start = time.time()
#使用sleep
time.sleep(random() * 2)
end = time.time()
#print('完成任务:{} 用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid()))
return '完成任务:{} 用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid())
container = []
def callback_func(n):#回调函数必须有参数 n接收的是task返回的东西
container.append(n)
if __name__ == '__main__':
pool = Pool(5)
tasks = ['听音乐', '吃饭', '洗衣服', '打游戏', '跑步', '看电影', '做饭']
for task1 in tasks:
pool.apply_async(task, args=(task1, ), callback=callback_func)# 非阻塞式 args进程名 callback = 回调函数名称
pool.close() #添加任务结束
pool.join() #join挡着主进程,只有进程结束主进程才能运行下一步
for c in container:
print(c)
print('over!!!!')
输出:
开始做任务啦! 听音乐
开始做任务啦! 吃饭
开始做任务啦! 洗衣服
开始做任务啦! 打游戏
开始做任务啦! 跑步
开始做任务啦! 看电影
开始做任务啦! 做饭
完成任务:跑步 用时:0.04454660415649414, 进程id:2960
完成任务:看电影 用时:0.2829704284667969, 进程id:2960
完成任务:洗衣服 用时:0.4715077877044678, 进程id:2376
完成任务:听音乐 用时:1.3277170658111572, 进程id:4556
完成任务:做饭 用时:1.012617826461792, 进程id:2960
完成任务:打游戏 用时:1.4509544372558594, 进程id:12852
完成任务:吃饭 用时:1.9367280006408691, 进程id:8828
over!!!!
#2、阻塞式
'''
特点:添加一个执行一个任务,如果一个任务不结束另一个任务不添加,如果执行过的任务数量达到指定的最大值,则下一个任务则从头开始
'''
def task(task_name):
print('开始做任务啦!', task_name)
start = time.time()
#使用sleep
time.sleep(random() * 2)
end = time.time()
print('完成任务:{} 用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid()))
#return '完成任务:{} 用时:{}, 进程id:{}'.format (task_name, (end - start), os.getpid())
if __name__ == '__main__':
pool = Pool(5)
tasks = ['听音乐', '吃饭', '洗衣服', '打游戏', '跑步', '看电影', '做饭']
for task1 in tasks:
pool.apply(task, args=(task1, ))# apply(self, func, args=(), kwds={}) 阻塞式
pool.close()
pool.join()
print('over!!!!')
输出:
开始做任务啦! 听音乐
完成任务:听音乐 用时:0.040094614028930664, 进程id:12916
开始做任务啦! 吃饭
完成任务:吃饭 用时:1.3161735534667969, 进程id:7212
开始做任务啦! 洗衣服
完成任务:洗衣服 用时:0.17168283462524414, 进程id:9800
开始做任务啦! 打游戏
完成任务:打游戏 用时:0.23629164695739746, 进程id:8140
开始做任务啦! 跑步
完成任务:跑步 用时:1.9264283180236816, 进程id:15856
开始做任务啦! 看电影
完成任务:看电影 用时:1.7075746059417725, 进程id:12916
开始做任务啦! 做饭
完成任务:做饭 用时:0.21764564514160156, 进程id:7212
over!!!!
总结:
'''
进程池:
pool = Pool(max) 创建进程池对象
添加任务:
pool.apply() 阻塞式
pool.apply_async() 非阻塞式
pool.close() 停止添加任务
pool.join() 让主进程让步,插队
'''
进程间的通信
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()) #qsize 队列长度
if not q.full(): #q.full()判断队列是否满了 q.empty() 判断队列是否为空
q.put('F', timeout=3) #put()如果queue满了则只能等待,除非有‘空地’则添加成功 put(self, obj, block=True, timeout=None)
else:
print('队列已满')
# #取队列的值
print(q.get(timeout=2)) #get(self, block=True, timeout=None) timeout超时 block阻塞
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
print(q.get(timeout=2))
#queue.join()阻塞至队列中所有的元素都被接收和处理完毕。当条目添加到队列的时候,未完成任务的计数就会增加。每当消费者线程调用 task_done() 表示这个条目已经被回收,该条目所有工作已经完成,未完成计数就会减少。当未完成计数降到零的时候, join() 阻塞被解除。
#queue.put(item, block=True, timeout=None) 将 item 放入队列。如果可选参数 block 是 true 并且 timeout 是 None (默认),则在必要时阻塞至有空闲插槽可用。如果 timeout 是个正数,将最多阻塞 timeout 秒,如果在这段时间没有可用的空闲插槽,将引发 Full 异常。反之 (block 是 false),如果空闲插槽立即可用,则把 item 放入队列,否则引发 Full 异常 ( 在这种情况下,timeout 将被忽略)。
#queue.get(self, block=True, timeout=None)从队列中移除并返回一个项目。如果可选参数 block 是 true 并且 timeout 是 None (默认值),则在必要时阻塞至项目可得到。如果 timeout 是个正数,将最多阻塞 timeout 秒,如果在这段时间内项目不能得到,将引发 Empty 异常。反之 (block 是 false) , 如果一个项目立即可得到,则返回一个项目,否则引发 Empty 异常 (这种情况下,timeout 将被忽略)。
#queue.put_nowait() 相当于 put(item, block=False)。
#queue.get_nowait() 相当于 get(item, block=False) 。
例:
from multiprocessing import Process,Queue
from time import sleep
def download(q):
imanges = ['girl.jpg', 'boy.jpg', 'man.jpg']
for imange in imanges:
print('正在下载:', imange)
sleep(0.5)
q.put(imange)
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()
print('000000000')
输出:
正在下载: girl.jpg
正在下载: boy.jpg
正在下载: man.jpg
girl.jpg保存成功
boy.jpg保存成功
man.jpg保存成功
全部保存完毕
000000000
二 多线程
'''
创建线程
t = threading.Thread(target=download, name='aa', args=(1,))
t.start()
线程状态:
新建 就绪 运行 阻塞(阻塞结束后回到就绪状态) 结束
'''
threading模块
import threading
#进程:Process
# 线程:Thread
from time import sleep
def download(n):
images = ['girl.jpg', 'boy.jpg', 'man.jpg']
for image in images:
print('正在下载:', image)
sleep(n)
print('下载{}成功'.format(image))
def listenMusic():
musics = ['小星星', '土耳其冰激凌', '烤面筋', '烤香肠']
for music in musics:
sleep(0.5)
print('正在听{}!'.format(music))
if __name__ == '__main__':
# 线程对象
t = threading.Thread(target=download, name='aa', args=(1,))
t.start()
t = threading.Thread(target=listenMusic, name='aa')
t.start()
n = 1
输出:
正在下载: girl.jpg
正在听小星星!
正在听土耳其冰激凌!
下载girl.jpg成功
正在下载: boy.jpg
正在听烤面筋!
下载boy.jpg成功
正在下载: man.jpg
正在听烤香肠!
下载man.jpg成功
import threading
'''
线程可以共享全局变量
'''
money = 1000
def run1():
global money
for i in range(100):
money -= 1
def run2():
global money
for i in range(100):
money -= 1
if __name__ == '__main__':
th1 = threading.Thread(target=run1, name='thread1')
th2 = threading.Thread(target=run2, name='thread2')
th1.start()
th2.start()
#插队
th1.join()
th2.join()
print('money:', money)
'''
线程同步
GIL 全局解释器锁
'''
import threading
n = 0
def task1():
global n
for i in range(1000000):
n += 1
print('task1中n的值是:', n)
def task2():
global n
for i in range(1000000):
n += 1
print('task2中n的值是:', n)
if __name__ == '__main__':
th1 = threading.Thread(target=task1)
th2 = threading.Thread(target=task2)
th1.start()
th2.start()
th1.join()
th2.join()
print('最后打印n:', n)
输出:
task1中n的值是: 1000000
task2中n的值是: 1998820
最后打印n: 1998820
三总结
'''
进程、线程:
Process类
创建进程:p = Process(target=func,name='',args=(),kwargs='')
启动进程: p.start()
自定义进程:
class MyProcess(Process):
def run(self):
pass
p = MyProcess()
p.start()
进程的数据共享
进程池: Pool
阻塞式: apply(func,args,kwargs)
非阻塞式: apply_async(func,args,kwargs,callback=函数)
进程间的通信:
Queue()
q = Queue(number)
q.put()
q.get()
q.qsize()
q.empty()
q.full()
线程:
包换关系
进程里存在多个线程,多个线程共用进程资源
t = Thread(target=func,name='',args=(),kwargs='')
t.start()
GIL 全局解释器锁
python的线程是伪线程
'''
四多线程同步
import threading
import random
import 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的值:', i)
time.sleep(0.5)
lock.release()
if __name__ == '__main__':
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
输出:
i的值: 0
i的值: 1
i的值: 2
i的值: 3
i的值: 4
i的值: 5
i的值: 6
i的值: 7
i的值: 8
i的值: 9
五死锁
资源分配不当,产生死锁
避免出现死锁
解决方式:
1、重构
2、acquire方法中加timeout参数
from threading import Thread,Lock
import time
lockA = Lock()
lockB = Lock()
class MyThread(Thread):
def run(self):
if lockA.acquire():#如果可以获取到锁则返回True
print(self.name+'获取了A锁')
time.sleep(0.1)
if lockB.acquire(timeout=5):
print(self.name+'又获取了B锁,原来还有A锁')
lockB.release()
lockA.release()
class MyThread1(Thread):
def run(self):
if lockB.acquire():#如果可以获取到锁则返回True
print(self.name+'获取了B锁')
time.sleep(0.1)
if lockA.acquire(timeout=5):
print(self.name+'又获取了A锁,原来还有B锁')
lockA.release()
lockB.release()
if __name__ == '__main__':
t1 = MyThread()
t2 = MyThread1()
t1.start()
t2.start()
输出:
Thread-1获取了A锁
Thread-2获取了B锁
Thread-1又获取了B锁,原来还有A锁
六生产者与消费者
两个线程之间的通信可以用队列实现
七协程
生成器完成
'''
greenlet完成协程是人工切换
'''
import time
from greenlet import greenlet
def a():#任务A
for i in range(5):
print('A' + str(i))
gb.switch()#切换到指定的线程
time.sleep(0.1)
def b():#任务B
for i in range(5):
print('B' + str(i))
gc.switch()
time.sleep(0.1)
def c():#任务C
for i in range(5):
print('C' + str(i))
ga.switch()
time.sleep(0.1)
if __name__ == '__main__':
ga = greenlet(a)
gb = greenlet(b)
gc = greenlet(c)
ga.switch()
gb.switch()
gc.switch()
输出:
A0
B0
C0
A1
B1
C1
A2
B2
C2
A3
B3
C3
A4
B4
C4
'''
gevent:当一个greenlet遇到IO(指的是input output输入输出等耗时操作)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO完成,在适当的时候切换回来)
gevent自动切换协程,保证总有greenlet在执行,而不是等待IO
'''
import time
import gevent as gevent
from gevent import monkey
monkey.patch_all()#打补丁
'''
猴子补丁将标准库的部分修补为与gevent友好的函数,这些函数的行为方式与原始库相同(至少尽可能地接近)。
猴子补丁将所有耗时的操作进行切换
'''
def a():#任务A
for i in range(5):
print('A' + str(i))
time.sleep(0.1)
def b():#任务B
for i in range(5):
print('B' + str(i))
time.sleep(0.1)
def c():#任务C
for i in range(5):
print('C' + str(i))
time.sleep(0.1)
if __name__ == '__main__':
g1 = gevent.spawn(a)
g2 = gevent.spawn(b)
g3 = gevent.spawn(c)
g1.join()
g2.join()
g3.join()
print('---------------')
输出:
A0
B0
C0
A1
B1
C1
A2
B2
C2
A3
B3
C3
A4
B4
C4
---------------