1. 守护进程与守护线程
-
无论是守护进程守护线程,都会在主进程或控制线程运行结束后终止,但不代表程序结束。
-
守护进程的特点:
-
主进程代码运行完毕,守护进程就会结束
-
主进程在程序代码全部运行结束后回收守护进程的资源,然后等待其他子进程运行结束后回收资源,直到所有子进程全部回收完再结束
-
只要主进程没有运行完,会一直运行守护进程
-
守护进程必须在子进程开启之前启用
-
守护进程不能再开启子进程
# 守护进程
import os
import time
from multiprocessing import Process
def task1():
print('task1 %s start...' % os.getpid())
time.sleep(1)
print('task1 %s stop...' % os.getpid())
def task2():
print('task2 %s start...' % os.getpid())
time.sleep(3)
print('task2 %s stop...' % os.getpid())
if __name__ == '__main__':
p1 = Process(target=task1)
p2 = Process(target=task2)
p1.daemon = True
p2.daemon = True
p1.start()
p2.start()
time.sleep(2)
print('yan...')
>>>
task1 2112 start...
task2 5844 start...
task1 2112 stop...
yan...
-
守护线程的特点:
-
控制线程所在的进程中所有非守护线程全部都运行完,守护线程才会结束
-
守护线程运行完而其他线程没有运行完,会先回收守护线程的资源,直到所有线程运行完之后回收所有资源才结束
-
守护线程没有运行完,而其他非守护线程已经运行完,会回收其他线程的资源后结束
# 守护线程
import time
from threading import Thread, current_thread
def task1():
print('task1 %s start...' % current_thread().getName())
time.sleep(1)
print('task1 %s stop...' % current_thread().getName())
def task2():
print('task2 %s start...' % current_thread().getName())
time.sleep(3)
print('task2 %s stop...' % current_thread().getName())
if __name__ == '__main__':
t1 = Thread(target=task1)
t2 = Thread(target=task2)
t1.daemon = True
t2.daemon = True
t1.start()
t2.start()
time.sleep(2)
print('yan...')
>>>
task1 Thread-1 start...
task2 Thread-2 start...
task1 Thread-1 stop...
yan...
2. 互斥锁
-
加锁:acquire()
-
释放锁:release()
-
with:加锁与释放锁
-
进程的互斥锁:
-
说明:进程之间数据无法共享,但是可以共享同一套系统、同一个文件、同一个终端,但是多进程由于处理时间的先后数序会出现结果错乱的情况,控制的方式就是加锁处理
-
不使用锁的情况:并发运行效率高,但是输出和修改的数据是错乱的,安全性不可靠
# 没有加锁的状态
import os
import time
import random
from multiprocessing import Process
def task():
print('%s ---> 111' % os.getpid())
time.sleep(random.randint(1, 3))
print('%s ---> 222' % os.getpid())
time.sleep(random.randint(1, 3))
print('%s ---> 333' % os.getpid())
if __name__ == '__main__':
p1 = Process(target=task)
p2 = Process(target=task)
p3 = Process(target=task)
p1.start()
p2.start()
p3.start()
print('yan...')
>>>
yan...
7772 ---> 111
4868 ---> 111
7980 ---> 111
7980 ---> 222
4868 ---> 222
7980 ---> 333
7772 ---> 222
4868 ---> 333
7772 ---> 333
-
使用锁的情况:加锁后由并发变成了串行,运行效率降低了,提高了安全性
# 加锁后的状态
import os
import time
import random
from multiprocessing import Process, Lock
def task(mutex):
mutex.acquire()
print('%s ---> 111' % os.getpid())
time.sleep(random.randint(1, 3))
print('%s ---> 222' % os.getpid())
time.sleep(random.randint(1, 3))
print('%s ---> 333' % os.getpid())
time.sleep(random.randint(1, 3))
mutex.release()
if __name__ == '__main__':
mutex = Lock()
p1 = Process(target=task, args=(mutex,))
p2 = Process(target=task, args=(mutex,))
p3 = Process(target=task, args=(mutex,))
p1.start()
p2.start()
p3.start()
print('yan...')
>>>
yan...
7512 ---> 111
7512 ---> 222
7512 ---> 333
9448 ---> 111
9448 ---> 222
9448 ---> 333
11672 ---> 111
11672 ---> 222
11672 ---> 333
# 模拟抢票
# 'db.txt文件内容:'{"count": 6}
import time
import random
import json
from multiprocessing import Process, Lock
def search():
with open('db.txt', encoding='utf-8') as f:
db_dict = json.load(f)
print('当前剩余票数是:%s' % db_dict['count'])
def rob():
with open('db.txt', encoding='utf-8') as fr:
db_dict = json.load(fr)
if db_dict['count'] > 0:
time.sleep(random.randint(1, 3))
db_dict['count'] -= 1
with open('db.txt', 'w', encoding='utf-8') as fw:
json.dump(db_dict, fw)
print('抢票成功!')
else:
print('没有票了...')
def task(mutex):
search()
with mutex:
rob()
if __name__ == '__main__':
mutex = Lock()
for i in range(10):
p = Process(target=task, args=(mutex,))
p.start()
>>> 'db.txt文件内容是:'{"count": 0}
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
当前剩余票数是:6
抢票成功!
抢票成功!
抢票成功!
抢票成功!
抢票成功!
抢票成功!
没有票了...
没有票了...
没有票了...
没有票了...
-
线程的互斥锁:
-
说明:线程之间本身就支持数据共享的,但是线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock仍然没有被释放则阻塞,即便是拿到执行权限GIL也要立刻交出来
import time
import random
from threading import Thread, current_thread, Lock
def task():
with mutex:
print('%s --> 111' % current_thread().getName())
time.sleep(random.randint(1, 3))
print('%s --> 222' % current_thread().getName())
time.sleep(random.randint(1, 3))
print('%s --> 333' % current_thread().getName())
time.sleep(random.randint(1, 3))
if __name__ == '__main__':
mutex = Lock()
t1 = Thread(target=task)
t2 = Thread(target=task)
t3 = Thread(target=task)
t1.start()
t2.start()
t3.start()
print('yan。。。')
>>>
Thread-1 --> 111
yan。。。
Thread-1 --> 222
Thread-1 --> 333
Thread-2 --> 111
Thread-2 --> 222
Thread-2 --> 333
Thread-3 --> 111
Thread-3 --> 222
Thread-3 --> 333
3. 信号量
-
信号量跟互斥锁的意义类似,互斥锁是多个进程或线程抢占一个,信号量是可以指定抢占多个
-
信号量跟池的不同是,信号量牵扯的是加锁的概念。池的只允许开启指定个数的进程或线程,而信号量是直接已经启用了多个进程或线程,通过设置的信号量来指定同时工作的进程或线程个数
# 进程的信号量
import os
import time
import random
from multiprocessing import Process, Semaphore
def task(sem):
with sem:
print('%s is doing...' % os.getpid())
time.sleep(random.randint(1, 3))
if __name__ == '__main__':
sem = Semaphore(4)
for i in range(10):
p = Process(target=task, args=(sem,))
p.start()
print('yan...')
>>>
8508 is doing...
10360 is doing...
10036 is doing...
yan...
4180 is doing...
5240 is doing...
8168 is doing...
3420 is doing...
10108 is doing...
10544 is doing...
9396 is doing...
# 线程的信号量
import time
import random
from threading import Thread, current_thread, Semaphore
def task(sem):
sem.acquire()
print('%s is doing...' % current_thread().getName())
time.sleep(random.randint(1, 3))
sem.release()
if __name__ == '__main__':
sem = Semaphore(3)
for i in range(10):
t = Thread(target=task, args=(sem, ))
t.start()
print('yan...')
>>>
Thread-1 is doing...
Thread-2 is doing...
Thread-3 is doing...
yan...
Thread-5 is doing...
Thread-4 is doing...
Thread-6 is doing...
Thread-7 is doing...
Thread-8 is doing...
Thread-9 is doing...
Thread-10 is doing...
4. 管道
-
说明:管道可以用于双向通信,利用通常在客户端/服务器中使用的请求响应模型或远程过程调用,就可以使用管道编写与进程交互的程序
-
作用:可以让进程之间通信
5. 队列
-
原理:管道和锁结合的方式实现,队列中可以存放所有数据类型的数据
-
作用:实现进程之间的通信
-
进程的队列需要从multiprocessing中导入Queue模块,线程的队列可以直接导入queue模块
-
特点:
-
队列:先进先出
-
堆栈:先进后出(后进先出)
-
优先级队列:根据指定的优先级输出,优先级低的先出(优先级队列是以元组的方式put,get出的结果也是元组)
-
堆栈和优先级只存在在线程的队列中
-
方法:
-
put():添加数据到队列中
-
get():从队列中读取并删除数据
-
empty():查看队列是否为空
-
full():查看队列是否已满
-
qsize():查看队里中的有效数据的数量
-
close():关闭队列
# 进程的队列
from multiprocessing import Queue
q = Queue(3)
q.put('yy')
q.put([1, 2, 3, 4, 5])
q.put({'name': 'yan', 'age': 18})
print(q.empty())
print(q.full())
print(q.get())
print(q.get())
print(q.get())
print(q.empty())
print(q.full())
>>>
False
True
yy
[1, 2, 3, 4, 5]
{'name': 'yan', 'age': 18}
True
False
# 线程的列队
import queue
q = queue.Queue(3)
q.put('yy')
q.put([1, 2, 3, 4, 5])
q.put({'name': 'yan', 'age': 18})
print(q.empty())
print(q.full())
print(q.get())
print(q.get())
print(q.get())
print(q.empty())
print(q.full())
>>>
False
True
yy
[1, 2, 3, 4, 5]
{'name': 'yan', 'age': 18}
True
False
# 线程的堆栈
import queue
q = queue.LifoQueue(3)
q.put('yy')
q.put([1, 2, 3, 4, 5])
q.put({'name': 'yan', 'age': 18})
print(q.get())
print(q.get())
print(q.get())
>>>
{'name': 'yan', 'age': 18}
[1, 2, 3, 4, 5]
yy
# 线程的优先级队列
import queue
q = queue.PriorityQueue(3)
q.put((0, 'yy'))
q.put((-1, [1, 2, 3, 4, 5]))
q.put((0.5, {'name': 'yan', 'age': 18}))
print(q.get())
print(q.get())
print(q.get())
>>>
(-1, [1, 2, 3, 4, 5])
(0, 'yy')
(0.5, {'name': 'yan', 'age': 18})
6. 生产者消费者模型
# 生产者:生产包子
# 消费者:吃包子
import os
import time
import random
from multiprocessing import Process, Queue
def producter(q):
for i in range(10):
res = '包子%s' % (i + 1)
time.sleep(1)
q.put(res)
print('<%s> 生产了 ---> [%s]' % (os.getpid(), res))
def consumer(q):
while True:
res = q.get()
if res == None:
break
print('<%s> 吃了 [%s]' % (os.getpid(), res))
time.sleep(random.randint(1, 3))
if __name__ == '__main__':
q = Queue()
p = Process(target=producter, args=(q, ))
c = Process(target=consumer, args=(q, ))
p.start()
c.start()
p.join()
q.put(None)
c.join()
print('yan...', os.getpid())
>>>
<11024> 生产了 ---> [包子1]
<3728> 吃了 [包子1]
<11024> 生产了 ---> [包子2]
<11024> 生产了 ---> [包子3]
<3728> 吃了 [包子2]
<11024> 生产了 ---> [包子4]
<11024> 生产了 ---> [包子5]
<3728> 吃了 [包子3]
<11024> 生产了 ---> [包子6]
<11024> 生产了 ---> [包子7]
<11024> 生产了 ---> [包子8]
<3728> 吃了 [包子4]
<11024> 生产了 ---> [包子9]
<11024> 生产了 ---> [包子10]
<3728> 吃了 [包子5]
<3728> 吃了 [包子6]
<3728> 吃了 [包子7]
<3728> 吃了 [包子8]
<3728> 吃了 [包子9]
<3728> 吃了 [包子10]
yan... 5124
7. 生产者消费者模型进阶
-
JoinableQueue:队列允许消费者通知生成者项目已经被成功处理;通知进程是使用共享的信号和条件变量来实现的。JoinableQueue对象的实例跟Queue对象的使用方法一样
-
maxsize:队列中允许最大值,默认为不限制
-
join():生产者调用之后进行阻塞,直到队列中所有的值都被处理完
-
task_done():消费者调用之后发出信号,表示队列中的值已经都处理完了
import os
import time
import random
from multiprocessing import Process, JoinableQueue
def producer(name, q):
for i in range(3):
res = '%s%s' % (name, i + 1)
time.sleep(1)
q.put(res)
print('<%s> 生产了 ---> [%s]' % (os.getpid(), res))
q.join()
def consumer(q):
while True:
res = q.get()
time.sleep(random.randint(1, 3))
print('<%s> 吃了 [%s]' % (os.getpid(), res))
q.task_done()
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer, args=('包子', q))
p2 = Process(target=producer, args=('油条', q))
p3 = Process(target=producer, args=('煎饼', q))
c1 = Process(target=consumer, args=(q,))
c2 = Process(target=consumer, args=(q,))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
p3.start()
c1.start()
c2.start()
p1.join()
p2.join()
p3.join()
print('yan...', os.getpid())
>>>
<200> 生产了 ---> [包子1]
<1084> 生产了 ---> [煎饼1]
<7680> 生产了 ---> [油条1]
<11748> 吃了 [包子1]
<200> 生产了 ---> [包子2]
<1084> 生产了 ---> [煎饼2]
<7680> 生产了 ---> [油条2]
<200> 生产了 ---> [包子3]
<1084> 生产了 ---> [煎饼3]
<7680> 生产了 ---> [油条3]
<11748> 吃了 [油条1]
<6972> 吃了 [煎饼1]
<11748> 吃了 [包子2]
<6972> 吃了 [煎饼2]
<11748> 吃了 [油条2]
<11748> 吃了 [煎饼3]
<11748> 吃了 [油条3]
<6972> 吃了 [包子3]
yan... 2312