1、进程间通信(二)
1、信号处理
signal.signal(signum,handler)
功能:处理一个信号
参数:
signum : 要处理的信号
handler: 对该信号的处理方法
SIG_DFL 采用默认方法
SIG_IGM 忽略这个信号
func 回调函数,自定义的方法处理
func 格式要求:
def 函数名(sig,frame)#必须两个形参
pass
sig:接收到的信号
frame:信号对象
示例1:
import signal
from time import sleep
signal.alarm(5)
# 采用默认的方法处理SIGALRM信号
# signal.signal(signal.SIGALRM, signal.SIG_DFL)
# 采用忽略信号方法
signal.signal(signal.SIGALRM, signal.SIG_IGN)
signal.signal(signal.SIGINT, signal.SIG_IGN)
while True:
sleep(2)
print('等待时钟……')
示例2
import signal
import time
# 固定格式要求
def handler(sig, frame):
if sig == signal.SIGALRM:
print('收到时钟信号')
elif sig == signal.SIGINT:
print('就不结束')
signal.alarm(7)
# 通过自定义方法处理
signal.signal(signal.SIGALRM, handler)
signal.signal(signal.SIGINT, handler)
while True:
print('waiting for a signal')
time.sleep(2)
*_*注:signal函数是一个异步处理信号函数,只要执行,在进程中就会按照制定方法处理信号
signal函数不能处理 SIGSTOP(暂停进程) SIGKILL(终止进程) 信号
2、信号量
给定一定的信号数量,对多个进程可见,并且多个进程均可操作。进程根据信号量的多少可以有不同的行为
multiprocessing 下Semaphore()方法,为处理信号量的方法
1、sem = Semaphore(num)
功能:定义信号量
参数:
num 给定信号量的初始个数,默认为1
返回值:
返回信号量的对象
2、sem.acquier()
功能:信号量减一
*注:信号量为0时,再调用函数会阻塞
3、sem.release()
功能:信号量加一
cookie:
1、multiprocessing.current_process()
获取当前进程对象2、使用信号处理僵尸进程
在父进程中,忽略子进程的发送信号signal(SIGCHLD,SIG_IGN)
示例:
from multiprocessing import Semaphore, Process, current_process
from time import sleep
# 创建信号量初始值为3
sem = Semaphore(3)
def fun():
print('进程%s等待信号量' % current_process())
# 第四个进程无信号资源会阻塞
sem.acquire()
print('进程%s消耗信号量' % current_process())
sleep(3)
print('进程%s添加信号量' % current_process())
sem.release()
jobs = []
for i in range(4):
p = Process(target=fun)
p.start()
jobs.append(p)
for i in jobs:
i.join()
2、同步和互斥
1、
目的:
对共有资源的操作会产生争夺,同步和互斥是一种解决争夺的方案临界资源
多个进程或线程都可以操作的资源临界区
操作临界资源的代码段同步
是一种合作关系为完成某个任务多进程或者多线程之间形成一种协调,按照条件次序执行,相互传递告知资源情况。
这种协调可能是因为阻塞关系达成的
互斥
是一种制约关系,当一个进程或者线程进入到临界区会进行加锁操作,此时其他进程(线程)再企图操作临界资源就会阻塞。
只有当资源被释放才能进行操作
2、同步互斥方法
1、Event 事件
from multiprocessing import Event1、e = Event()
创建事件对象2、e.wait()
提供事件阻塞3、e.set()
对事件对象进行设置,此时wait判断如果事件被set则结束阻塞4、e.clear()
清除对该对象事件的设置5、e.is_set()
检测对象是否被设置,设置返回True,事件阻塞为False示例:
# event_test.py
from multiprocessing import Event
# 创建事件对象
e = Event()
print(e.is_set())
e.set()
# 讲设置清除 wait又堵塞
e.clear()
e.wait()
print(e.is_set())
# process_event.py
from multiprocessing import Event, Process
import time
def wait_event():
print('process1也想操作临界区,但是要阻塞等待主进程')
e.wait()
print('主进程操作完了,轮到我了', e.is_set())
def wait_event_timeout():
print('process2也想操作临界区,但是要阻塞等待主进程')
e.wait(2)
print('process2等不了了,还是执行别的吧', e.is_set())
e = Event()
p1 = Process(name='block', target=wait_event)
p2 = Process(name='non-block', target=wait_event_timeout)
p1.start()
p2.start()
print('假设正在忙了的操作临界资源')
time.sleep(3)
e.set()
print('主进程操作完了,开放临界区')
p1.join()
p2.join()
2、锁 Lock
from multiprocessing import Lock1、lock=Lock()
创建对象2、lock.acquire()
上锁3、lock.release()
解锁4.
with lock: # 给with代码段上锁...
with 代码段结束自动解锁
*注:在lock对象处于上锁状态的时候,再企图上锁则会阻塞直到锁被释放,才能继续执行上锁操作
示例:
# process_lock.py
from multiprocessing import Process, Lock
import sys
import time
# sys.stdout为所有进程的共有资源
def worker1():
lock.acquire() # 上锁
for i in range(5):
time.sleep(1)
sys.stdout.write('worker1 输出\n')
lock.release() # 解锁
def worker2():
# 用with方式上锁
with lock:
for i in range(5):
time.sleep(1)
sys.stdout.write('worker2 输出\n')
# 创建Lock对象
lock = Lock()
w1 = Process(target=worker1)
w2 = Process(target=worker2)
w1.start()
w2.start()
w1.join()
w2.join()
3、线程
1、定义:
1、线程也是多任务变成的一种方法,可以使用计算机多核资源。是计算机核心分配的最小单位2、线程又称为出、轻量级的进程,在创建和删除时消耗的计算机资源小
2、线程和进程的关系:
1、一个进程中可以包含多个线程2、进程中的所有线程共享进程的空间资源(空间,全局变量,分配的内存等)
3、进程也有自己的特有属性,如指令集、TID等
3、创建线程:
import threadingthreaking.Tread()
1、Tread()
功能:创建线程
参数:
target 线程函数
args 元组 给线程函数位置传参
kwargs 字典 给线程函数字典传参
name 给线程取名字 (默认Thread-1)
返回:
返回一个线程对象
示例:
# thread1.py
import threading
import time
# 线程函数
def music():
while True:
time.sleep(2)
print('播放葫芦娃')
# 创建线程和函数music关联
t = threading.Thread(target=music)
# 启动线程
t.start()
while True:
time.sleep(1.5)
print('想听灌篮高手')
# 等待回收线程
t.join()
print('-------------------------')
4、线程对象属性
1、t.start()
启动线程2、t.join(timeout)
回收线程3、t.is_alive()
查看线程状态4、t.name
查看线程名称5、t.setName(name)
为线程设置名字参数:
name 线程名字
6、threading.currentThread()
得到线程对象可以使用getName()获得线程名称
7、t.daemon属性
默认为False 主线程执行完毕不会影响分支线程的执行设置为True 主线程执行完毕其他线程也会终止
设置方法
t.daemon=True
t.setDaemon(True)
判断daemon属性是True or False
t.isDaemon():
示例:
# 线程对象属性
from threading import Thread, currentThread
from time import sleep
def fun(sec):
print('线程属性测试')
sleep(sec)
print('%s线程结束' % currentThread().getName())
thread = []
for i in range(3):
t = Thread(name='tedu%d' % i, target=fun, args=(3,))
t.start()
thread.append(t)
for i in thread:
print('thread name:', i.name)
print('slive:', i.is_alive())
i.join()
from threading import Thread
from time import sleep
def fun():
print('Daemon 属性测试')
sleep(5)
print('线程结束')
t = Thread(target=fun)
# 为线程设置名字
t.setName('tedu')
# daemon属性设置为True
# t.setDaemon(True)
t.daemon = True
print(t.isDaemon())
t.start()
t.join(2)
print('=======主线程结束========')
4、线程的通信
通过全局变量通信示例:
from threading import Thread
from time import sleep
# 通过全局变量进行线程通信
a = 1
def foo():
global a
a = 1000
def bar():
sleep(1)
print('a =', a) # a = 1000
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.start()
t2.start()
t1.join()
t2.join()
5、进程和线程的区别和联系
1、两者都是多任务编程方式,均可使用计算机的多核。2、进程的创建要比线程消耗更多的资源。
3、进程空间独立,数据安全性更好操作,有专门的进程间通信方式。
4、线程使用全局变量通信,往往要和同步互斥机制配合防止产生资源的争夺。
5、一个进程可以包含多个线程,线程共享进程资源。
6、进程线程都有自己的特有资源。
6、使用场景:
需要常见较多的并发,任务比较简单,线程比较合适如果程序间数据资源使用重叠比较多,要考虑到线程锁是否需要更复杂的逻辑
如果多个任务并无关联,不易用于多线程将其融入到一个进程中
Python线程不适用于计算密集型并发
7、创建自定义线程类
1.继承Thread类2.重写 run 方法
示例:
from threading import Thread
from time import sleep, ctime
# 编写自己的线程类
class MyThread(Thread):
def __init__(self, func, args, name='Tedu'):
super().__init__()
self.func = func
self.args = args
self.name = name
# 调用start会自动运行
def run(self):
self.func(*self.args)
def player(file, sec):
for i in range(2):
print('playing %s : %s' % (file, ctime()))
sleep(sec)
t = MyThread(player, ('baby.mp3', 3))
t.start()
t.join()
10、线程中同步互斥方法(一)
1、Event 线程事件
1、e = Event()
创建事件对象2、e.wait()
提供事件阻塞3、e.set()
对事件对象进行设置,此时wait判断如果事件被set则结束阻塞4、e.clear()
清除对该对象事件的设置5、e.is_set()
检测对象是否被设置,设置返回True,事件阻塞为False示例:
import threading
from time import sleep
s = None
# 创建线程对象
e = threading.Event()
def bar():
print('呼叫foo')
global s
s = '天王盖地虎'
def foo():
print('等待口令')
sleep(2)
if s == '天王盖地虎':
print('foo收到:', s)
else:
print('打死他')
e.set()
def fun():
print('呵呵')
sleep(1)
e.wait()
global s
s = '小鸡炖蘑菇'
t1 = threading.Thread(name='bar', target=bar)
t2 = threading.Thread(name='foo', target=foo)
t3 = threading.Thread(name='fun', target=fun)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
'''
运行结果:
呼叫foo
等待口令
呵呵
foo收到: 天王盖地虎
'''
2、Lock 线程锁
lock = Lock() 创建锁lock.acquire() 加锁
lock.release() 解锁
可以使用with加锁解锁
示例:
import threading
a = b = 0
lock = threading.Lock()
def value():
while True:
lock.acquire()
if a != b:
print('a = %d,b = %d' % (a, b))
lock.release()
t = threading.Thread(target=value)
t.start()
while True:
lock.acquire()
a += 1
b += 1
lock.release()
t.join()