Python 并发编程 (守护进程, 进程同步(multiprocessing.Lock、Semaphore、Event)

守护进程(p.daemon = True)将p设置为守护进程
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止(主进程和子进程是异步的),当主进程停止,该守护进程不在继续执行.守护进程也是一种子进程.
主进程创建守护进程
  其一:守护进程会在主进程代码执行结束后就终止.(但本质上是在主进程结束之前结束的,主进程需要负责回收资源)
  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

from multiprocessing import Process
import time
import os

def func(num):
    print(f'{num},pid:{os.getpid()},ppid:{os.getppid()}')
    while True:
        print('is alive')
        time.sleep(0.5)

def wahaha():
    i = 0
    while i lt; 5:
        i += 1
        print(f'第{i}秒')
        time.sleep(1)


if __name__ == '__main__':
    Process(target=wahaha).start()  #子进程在主进程结束后仍然正常执行
    p = Process(target=func,args=(1,))
    p.daemon = True #主进程结束,该守护进程结束,将p设置为守护进程
    p.start()
    time.sleep(3)
    print(f'pid:{os.getpid()},ppid:{os.getppid()}')

    print('主进程结束')

执行结果:

11,pid:8200,ppid:2000
is alive
is alive
第2is alive
is alive
第3is alive
is alive
pid:2000,ppid:7244
主进程结束
第4秒
第5

创建守护进程

多进程中的方法
p = Process(target=func,args=(1,)) #创建一个进程对象
p.start()  启动一个进程
p.daemon = True  设置进程为守护进程,随主进程结束而结束.
p.is_alive()  判断进程是否存活,返回bool值
p.terminate()  发送给操作系统指令,关闭进程
p.pid()查看进程pid

另一种执行/编写方式

from multiprocessing import Process
import time
import os

def func(num):
    print(f'{num},pid:{os.getpid()},ppid:{os.getppid()}')
    while True:
        print('is alive')
        time.sleep(0.5)

def wahaha():
    i = 0
    while i < 10:
        i += 1
        print(f'第{i}秒')#f表示print的格式化输出,待查询
        time.sleep(1)


if __name__ == '__main__':
    p2 = Process(target=wahaha)
    p2.start()

    p = Process(target=func,args=(1,))
    p.daemon = True #主进程结束,该子进程结束
    p.start()
    time.sleep(3)
    print(p.is_alive())
    print(p2.is_alive())
    p2.terminate()
    time.sleep(0.1)
    print(p.is_alive())
    print(p2.is_alive())
    print(f'pid:{os.getpid()},ppid:{os.getppid()}')
    print('主进程结束')
    p2.join()

Process方法//看不懂下面

import socket
from multiprocessing import Process

def talk(conn,addr):
    while True:
        msg_r = conn.recv(1024).decode('utf-8')
        print(addr,msg_r)
        msg_s = 'client{}登陆'.format(addr)
        conn.send(msg_s.encode('utf-8'))
    conn.close()


if __name__ == '__main__':
    sk = socket.socket()
    sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sk.bind(('127.0.0.1',8091))
    sk.listen(5)
    try:
        while True:
            conn,addr = sk.accept()
            Process(target=talk,args=(conn,addr)).start()
    finally:
        sk.close()

socket多进程server

import socket


sk = socket.socket()
sk.connect(('127.0.0.1',8091))

while True:
    msg_s = input('请输入内容:')
    sk.send(msg_s.encode('utf-8'))
    msg_r = sk.recv(1024).decode('utf-8')
    print(msg_r)


sk.close()

socket多进程client

进程同步(multiprocessing.Lock、Semaphore、Event)
锁 multiprocessing.Lock()
  避免同一段代码被多个进程同时执行
lock = Lock()  创建锁对象
lock.acquire()  查询钥匙,如果有就拿走,如果没有就等待
lock.release()  归还钥匙
lock可以使用with上下文进行管理(类似于文件读取)//未理解

with lock:
  print('hello' )

维护数据的安全
降低了程序的效率
所有的效率都是建立在数据安全的角度上的
但凡涉及到并发编程都要考虑数据的安全性
我们需要在并发部分对数据修改的操作格外小心,如果会涉及到数据的不安全,就需要进行加锁控制

lock 内部实现了进程之间的通信,使得谁acquire了谁release了能够在多个拥有lock参数的子进程中透明(类似于PV操作)

from multiprocessing import Lock
lock = Lock()  #创建一个锁对象
lock.acquire() #想拿钥匙,如果有就拿,没有就一直等
print('拿到要钥匙了1')
lock.release() #还钥匙
lock.acquire() #想拿钥匙
print('拿到要钥匙了2')
lock.release() #还钥匙

开启一个进程锁

#db文件内容  {"count": 0}

import json
import time
from multiprocessing import Process,Lock
def search(i):
    f =open('db')
    ticket_dic =json.load(f)
    f.close()
    print(f"{i} 正在查票,剩余票数{ticket_dic['count']}")

def buy(i):
    with open('db') as f: ticket_dic = json.load(f)
    time.sleep(0.2)
    if ticket_dic['count'] > 0:
        ticket_dic['count'] -= 1
        print(f'{i} 买到票了')
        time.sleep(0.2)
        with open('db','w') as f :json.dump(ticket_dic,f)
    else:
        print(f"{i} 太火爆被抢购一空了,剩余票数{ticket_dic['count']}")


# def get_ticket(i,lock):
#     search(i)
#     lock.acquire()
#     buy(i)
#     lock.release()

def get_ticket(i,lock):
    search(i)
    with lock:
        buy(i)

if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        p = Process(target=get_ticket,args=(i,lock))
        p.start()

火车票查询购买

信号量(标志TrueFalse) multiprocessing.Semaphore() (锁+计数器)
有多个钥匙的锁

互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据 。
假设商场里有4个迷你唱吧,所以同时可以进去4个人,如果来了第五个人就要在外面等待,等到有人出来才能再进去玩。
实现:
信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。
信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念

信号量
sem = Semaphore(4)  创建锁对象,4把钥匙,可以被连续acquire4次
sem.acquire()  查询钥匙,如果有就拿走,如果没有就等待
sem.release()  归还钥匙
sem 可以使用with上下文进行管理(类似于文件读取)

with sem:#待测试
  print('hello' )
"""
可能相当于在代码块前后分别加了acquire和release
"""
from multiprocessing import Semaphore
sem = Semaphore(4) #4把钥匙
sem.acquire()#1
print(1)
sem.acquire()#2
print(2)
sem.release()#1
sem.acquire()#2
print(3)
sem.acquire()#3
print(4)
sem.acquire()#4
print(5)
sem.acquire()#5
print(6)
from multiprocessing import Semaphore,Process
import time
import random

# def ktv(sem,i):#方法一
#     sem.acquire()
#     print(f'{i}走进ktv')
#     time.sleep(random.randint(1,3))
#     print(f'{i}走出ktv')
#     sem.release()

def ktv(sem,i):#方法二
    with sem:
        print(f'{i}走进ktv')
        time.sleep(random.randint(1,3))
        print(f'{i}走出ktv')


if __name__ == '__main__':
    sem = Semaphore(4)
    for i in range(10):
        p = Process(target=ktv,args=(sem,i))
        p.start()

事件 multiprocessing.Event()
控制子进程执行还是阻塞的一个机制
e = Event()  创建一个事件对象
Event方法在事件中有一个信号(标志)
wait() 如果这个标志是True,那么wait的执行效果就是pass ,如果是False,那么wait方法的效果就是阻塞,直到这个标志变成True。
控制标志方法
is_set()  判断标志的状态,返回bool值
set()  将标志设置为True
clear()  将标志设置为False

from multiprocessing import Event
e = Event() #阻塞,事件的创建之初标志的状态是False
print(e.is_set())
e.set() #将标志改为True
print(e.is_set())
e.wait() #当标志为True是pass,不阻塞

创建一个事件

from multiprocessing import Event,Process
import time

def func1(e):
    print('start func1')
    print(e.is_set())  #事件创建之初是False
    e.wait(1)  #不修改状态(网络测试,发送短信,发送邮件),超时后继续执行,不继续阻塞
    print(e.is_set())
    e.wait()  #持续阻塞
    print(e.is_set()) #主进程3(异步)s后修改信号标志为True ,继续执行
    print('end func1')

if __name__ == '__main__':
    e = Event()
    Process(target=func1,args=(e,)).start()
    time.sleep(3)
    e.set()

事件的控制

from multiprocessing import Event,Process
import time
import random

def tarffic_light(e):
    while True:
        while e.is_set():
            print('绿灯亮')
            time.sleep(2)
            e.clear()
        else:
            print('红灯亮')
            time.sleep(2)
            e.set()


def car(i,e):
    while not e.is_set():
        print(f'{i}正在等待通过...')
        e.wait()
    else:
        print(f'{i}通过.')

if __name__ == '__main__':
    e = Event()
    light = Process(target=tarffic_light,args=(e,))
    light.daemon =True
    light.start()
    car_list = []
    for i in range(1,21):
        p = Process(target=car,args=(i,e))
        car_list.append(p)
        p.start()
        time.sleep(random.randint(0,3))
    for i2 in car_list:i2.join()  #控制子进程先执行完毕
    print('执行完啦')

while版红绿灯

import time
import random
from multiprocessing import Process,Event
def traffic_light(e):
    print('红灯亮')
    while True:
        time.sleep(2)
        if e.is_set():
            print('红灯亮')
            e.clear()
        else:
            print('绿灯亮')
            e.set()

def car(i,e):
    if not e.is_set():
        print('car%s正在等在通过'%i)
        e.wait()
    print('car%s通过'%i)

if __name__ == '__main__':
    e = Event()
    light = Process(target=traffic_light,args=(e,))
    light.daemon = True
    light.start()
    car_lst = []
    for i in range(20):
        p = Process(target=car,args=(i,e))
        p.start()
        time.sleep(random.randint(0,3))
        car_lst.append(p)
    for car in car_lst:car.join()

if 红绿灯
说明:红绿灯的的变化和汽车的通行是两个独立的进程,汽车通过对红绿灯的事件信号的查询判断等待和放行,每一个汽车都是独立的进程

进程间通信(进程之间数据共享)
队列multiprocessing.Queue (先进先出)队列是基于(管道+锁)实现的
创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
Queue([maxsize])
创建共享的进程队列。
参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
底层队列使用管道和锁定实现。
  q =Queue()  创建一个队列 q = Queue(5) 队列长度为5
  q.put(1)  向队列中放一个数据,可以是int,list,dict …当队列满时会阻塞
  q.get()  从队列中获取一个数据 没有值会一直阻塞
  q.empty()  判断队列是否为空返回bool值   多进程时不准 ,如果其他进程或线程正在往队列中添加项目,结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。注重进程访问控制
  q.full()  判断队列是否已满返回bool值  多进程时不准由于线程的存在,结果也可能是不可靠的。注重进程访问控制
  q.qsize()返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。注重进程访问控制
  q.close()关闭队列,防止队列中加入更多数据。调用此方法时,后台线程将继续写入那些已入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将自动调用此方法。关闭队列不会在队列使用者中生成任何类型的数据结束信号或异常。例如,如果某个使用者正被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
  q.cancel_join_thread()不会再进程退出时自动连接后台线程。这可以防止join_thread()方法阻塞。
  q.join_thread()连接队列的后台线程。此方法用于在调用q.close()方法后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread()方法可以禁止这种行为。

q = Queue(3)
try:
    q.get_nowait()#就算被锁了,也往外拿出,除非empty
except:
    print('队列中没有值')

q.get_nowait()


q = Queue(3)
q.put(1)
q.put('aaa')
q.put([1,2,3])
# q.put('alex')   #队列满会阻塞,就会一直卡在那里,对单进程丝毫没有体现作用,需要多进程,另一个进程get一个数据,此命令才可能执行
try:
    q.put_nowait('alex')#就算被锁了,也往里加入,除非full
except:
    print('丢失了一个数据')

q.put_nowait()

创建一个队列

from multiprocessing import Process,Queue
def func(num,q):
    q.put({num:num**num})

if __name__ == '__main__':
    q = Queue()
    # p = Process(target=func, args=(10,q))
    # p.start()
    # print(q.get())
    for i in range(10):
        p = Process(target=func,args=(i,q))
        p.start()
    for i in range(10):
        print(q.get())

=============
{0: 1}
{1: 1}
{3: 27}
{4: 256}
{2: 4}
{5: 3125}
{9: 387420489}
{8: 16777216}
{6: 46656}
{7: 823543}

使用Queue队列特性使用put,get解决返回值问题

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值