Python学习 之 进程


开启子进程

方式1: 直接定义子进程代码

from multiprocessing import Process


# 直接定义进程代码
def task(name): pass


if __name__ == "__main__":
    # 启动进程的方式 1
    p1 = Process(target=task, args=("Tim",)) # 这里 args 参数是一个元祖类型, 所以必须加逗号
    # 向系统发信号, 请求内存资源
    p2.start()
    
    # 多进程示例
    p2 = Process(target=task, args=(1,))

    l = [p1, p2]
    
    for i in l:
        i.start()

    for i in l:
        i.join()

    print("主进程结束")


方式2: 自定义类

from multiprocessing import Process

# 继承 Process 模块, 将进程代码写入 run()方法
class MyProcess(Process):
    # 利用父类的代码进行初始化
    def __init__(self):
        super().__init__()

    def run(self, name): pass


if __name__ == "__main__":
    # 启动进程的方式 2
    p2 = MyProcess()
    p2.run("Tim")


进程常见方法

""" 
	join()			阻塞
	is_alive()		存活状态
	name			进程的名字, 默认为Process-1, Process-2

"""

from multiprocessing import Process
import time

def task(name):
    print("1 %s is running" % name)
    time.sleep(1)
    print("1 %s is done" % name)


if __name__ == "__main__":
    p1 = Process(target=task, args=("Tim",))
    p2 = Process(target=task, args=("Tom",), name="Tom-Process")
    
    p1.start()
    p2.start()

    # 查看进程是否存活
    print(p1.is_alive()) # True

    # 查看进程名
    print(p1.name, p2.name) # Process-1 Tom-Process

    # 阻塞主进程, 当子进程运行结束, 发送信号给 join(), 释放主进程阻塞
    p2.join()
    p1.join()

    print("main end") # 这行代码会等p1和p2结束后再执行
    

守护进程

  • 守护进程必须在 start() 之前设置
  • 守护进程里不能再开子进程
  • 主进程: 皇帝; 主进程结束: 皇帝驾崩
  • 守护进程:贴身太监; 守护进程随主进程结束而结束: 陪葬
  • 守护进程不能开子进程: 太监无后
from multiprocessing import Process
import time


def task(n):
    print("%s: start" % n)
    time.sleep(2)
    print("%s: end" % n)


if __name__ == "__main__":
    p = Process(target=task, args=("aaa",))
    # 进程 p 设置为守护进程
    p.daemon=True 
    p.start()

    print("main end")


示例 - 守护进程报活

from multiprocessing import Process
import time


def task():
    # 正常函数, 每 1s 打印一次
    for i in range(5):
        print("task %s" % i)
        time.sleep(1)


def task_d():
    # 守护进程, 每0.5s 报活一次
    while 1:
        print("主进程仍然存活..")
        time.sleep(0.5)
        

if __name__ == "__main__":
    # 开启子进程
    p = Process(target=task)
    # 开启一个守护进程, 本例中, 守护进程会随主进程关闭而关闭
    pd = Process(target=task2)

	# 守护进程的开启
    pd.daemon = True

    p.start()
	pd.start()

    # 1. 写上 p.join(), main end 会等待 p 结束后再打印
    # 2. 不写 p.join(), main end 不会等待 p 结束后再打印
    # 所以 p.join 写不写取决于逻辑是否需要按顺序执行 
    p.join()

    print("main end")
    

进程锁 - 保证数据安全

from multiprocessing import Process,Lock
import json
import time
import random
import os

def search():
	# 模拟网络延迟
    time.sleep(random.randint(1,3))
    # 模拟查找数据
    dic = json.load(open("db.txt","r",encoding="utf-8"))
    print("%s 查看剩余票数%s" % (os.getpid(), dic["count"]))


def get():
	# 模拟查找数据
    dic = json.load(open("db.txt","r",encoding="utf-8"))
    # 判断如果有, 数量减一
    if dic["count"] > 0:
    	# 模拟后台操作数据库
        dic["count"] -= 1
        json.dump(dic, open("db.txt","w",encoding="utf-8"))
        # 模拟网络延迟
        time.sleep(random.randint(1,3))
        print("%s 购票成功" % os.getpid())
    else:
        print("购票失败")


def task(m):
	# 查找时, 因为并不对数据做修改, 所以不需要加锁 
    search()
    # 加锁, 因为会涉及到数据变化
    m.acquire()
    get()
    # 解锁
    m.release()


if __name__ == "__main__":
    # 用一把锁, 锁住公用数据
    mutex = Lock()
    for i in range(10):
        p = Process(target=task, args=(mutex,)) # 将锁作为参数传给子进程
        p.start()


进程队列

  • 因为进程间内存空间隔离, 所以通信需要 IPC 处理机制
    • 处理内存空间隔离问题
    • 处理数据加锁问题

创建队列

from multiprocessing import Queue

# 长度为 3 的队列, 不写则没有长度限制
q = Queue(3)


# 放入, 可以接收所有类型的数据
q.put("1")
q.put(2)
q.put((3,))

# block: 是否阻塞等待, 两个写法等价
q.put({"count": 4}, block=False)
q.put_nowait({"count": 4})

# timeout: 为阻塞设置超时时间
q.put([5,], timeout=3)


# 取出, 参数和 put()使用方法相同
print(q.get())
print(q.get())
print(q.get())


生产者消费者模型 (Queue版本)

from multiprocessing import Process, Queue
import time
import random


def producer(name,  food, q):
    for i in range(3):
        # 模拟耗时
        time.sleep(random.randint(1, 3))
        # 生产包子, 放入队列
        r = "包子%s" % i
        q.put(r) 
        print("厨师[%s] 生产了<%s%s>" % (name, food, i))


def consumer(name, q):
    while 1:
        # 模拟耗时
        time.sleep(random.randint(1,3))
        # 拿出包子
        r = q.get()
        if r is None:
            break
        print("吃货[%s] 吃了<%s>" % (name, r))


if __name__ == "__main__":
    # 队列
    q = Queue()

    # 生产者们: "老王" 造 "包子" 放入 "队列"
    p1 = Process(target=producer, args=("老王", "包子", q))
    p2 = Process(target=producer, args=("小王", "馒头", q))

    # 消费者们: "老张" 从 "队列" 取东西吃 
    c1 = Process(target=consumer, args=("老张1",  q))

    p1.start()
    p2.start()
    c1.start()

    # join() 保证生产完成, 然后放入结束信号
    # 有几个消费者就需要几个结束符号, 本例只有一个消费者, 所以放入一个即可
    p1.join()
    q.put(None)
    
    print("end main")
	    

生产者消费者模型(JoinableQueue 版本)

from multiprocessing import Process, JoinableQueue
import time
import random


def producer(name,  food, q):
    for i in range(3):
        # 模拟耗时
        time.sleep(random.randint(1, 3))
        # 生产包子, 放入队列
        r = "包子%s" % i
        q.put(r) 
        print("厨师[%s] 生产了<%s%s>" % (name, food, i))


def consumer(name, q):
    while 1:
        # 模拟耗时
        time.sleep(random.randint(1,3))
        # 拿出包子
        r = q.get()
        print("吃货[%s] 吃了<%s>" % (name, r))
        # 3. 每取走一次数据, 发送一次"-1"信号, 当队列中没有数据时, 执行 q.join()之后的
        # 猜想是在生产者执行 q.put() 时进行计数
        q.task_done()


if __name__ == "__main__":
    # 队列
    q = JoinableQueue()

    p1 = Process(target=producer, args=("老王", "包子", q))
    p2 = Process(target=producer, args=("小王", "馒头", q))

    c1 = Process(target=consumer, args=("老张1",  q))
    c2 = Process(target=consumer, args=("老张2",  q))
    c3 = Process(target=consumer, args=("老张3",  q))
   
    # 5. 设置守护进程
   	c1.daemon = True
    c2.daemon = True
    c3.daemon = True

    p1.start()
    p2.start()
    c1.start()
    c2.start()
    c3.start()

	# 1. join() 保证生产完成, 此时生产者结束
    p1.join()
    p2.join()
	
	# 2. 阻塞, 等待队列被取空
    q.join()

	# 4. 此时主进程结束, 但是消费者子进程没有结束, 将它们设置成守护进程使其同时结束即可
    print("end main")

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值