Python3并发编程-多进程multiprocessing

目录

1:子进程创建

1.1:Process类创建子进程

1.2:Process子类创建子进程

2:Process类中的方法

2.1:join()方法:等待子进程结束

2.2:terminate()方法:终止进程工作

2.3:守护进程:daemon属性

3:进程间通信与同步

3.1:管道Pipe

3.2:队列Queue

3.3:队列JoinableQueue

3.4:共享数据:Managers

3.5:信号量:Semaphore

3.6:事件:Event

3.7:锁:Lock

3.8:进程池:Pool


进程之间是相互独立的,各个进程之间的数据是不能共享的。Python3中的multiprocess包提供了进程相关操作的类,如Process类等

1:子进程创建

1.1:Process类创建子进程

# -*- coding: utf-8 -*-

import os
import time
from multiprocessing import Process

# print(help(Process))

def func(pid):
    print(f'父进程:', pid)
    print(f'子进程:',os.getpid())
    print(f'子进程的父进程:',os.getppid())
    time.sleep(10)

if __name__ == '__main__':
    print(f'主进程的父进程ID号:{os.getppid()}')
    print(f'主进程的进程ID号:{os.getpid()}')
    #创建一个进程,target:新创建的这个进程会去执行func这个函数
    p1 = Process(target=func,args=(os.getpid(),))
    #启动进程
    p1.start()
    print('主进程结束')

1.2:Process子类创建子进程

# -*- coding: utf-8 -*-

import os
import time
from multiprocessing import Process


# print(help(Process))

# 需要实现类的run方法,在实例调用start()方法之后会自动调用run方法。
class MProcess(Process):

    def __init__(self,ppid): # 需要什么参数就传递什么
        super().__init__()  # 需要调用父类中的 __init__()方法
        self.ppid = ppid

    def run(self):
        print(f'父进程:', self.ppid)
        print(f'子进程:', os.getpid())
        print(f'子进程的父进程:', os.getppid())
        time.sleep(10)

if __name__ == '__main__':
    print(f'主进程的父进程ID号:{os.getppid()}')
    print(f'主进程的进程ID号:{os.getpid()}')
    #创建一个进程,target:新创建的这个进程要去执行func这个函数
    p1 = MProcess(os.getpid())
    #启动进程
    p1.start()
    print('主进程结束')

2:Process类中的方法

2.1:join()方法:等待子进程结束

查看帮助:
# print(help(p1))
# print(help(p1.join))
join(timeout=None) method of multiprocessing.context.Process instance
    Wait until child process terminates

# -*- coding: utf-8 -*-

import os
import time
from multiprocessing import Process

# print(help(Process))

def func(pid):
    print(f'父进程:', pid)
    print(f'子进程:',os.getpid())
    print(f'子进程的父进程:',os.getppid())
    time.sleep(10)
    print('子进程结束')

if __name__ == '__main__':
    print(f'主进程的父进程ID号:{os.getppid()}')
    print(f'主进程的进程ID号:{os.getpid()}')
    #创建一个进程,target:新创建的这个进程会去执行func这个函数
    p1 = Process(target=func,args=(os.getpid(),))
    # print(help(p1))
    print(help(p1.join))

    #启动进程
    p1.start()
    # p1.join(4) # 设置超时,超过时间子进程还没有结束,不会再等待下去
    p1.join()  # 一直阻塞在这里,直到子进程结束

    print('-'*30)
    print('主进程结束')

2.2:terminate()方法:终止进程工作

# -*- coding: utf-8 -*-

import os
import time
from multiprocessing import Process

# print(help(Process))

def func(pid):
    print(f'父进程:', pid)
    print(f'子进程:',os.getpid())
    print(f'子进程的父进程:',os.getppid())

    while True:
        time.sleep(1)
        print(f'子进程:', os.getpid())
        print(f'子进程的父进程:', os.getppid())
        print('-'*30)

    print('子进程结束')

if __name__ == '__main__':
    print(f'主进程的父进程ID号:{os.getppid()}')
    print(f'主进程的进程ID号:{os.getpid()}')
    #创建一个进程,target:新创建的这个进程会去执行func这个函数
    p1 = Process(target=func,args=(os.getpid(),))

    #启动进程
    p1.start()

    print('-'*30)
    time.sleep(10)
    p1.terminate()  # 结束子进程
    time.sleep(3)
    print('子进程状态:',p1.is_alive())
    print('主进程中-子进程id:', p1.pid)



    print('主进程结束')

2.3:守护进程:daemon属性

 

在主进程结束的时候,子进程也会终止;terminate()方法是主进程还在的时候,终止子进程。

# -*- coding: utf-8 -*-

import os
import time
from multiprocessing import Process

# print(help(Process))

def func(pid):
    print(f'父进程:', pid)
    print(f'子进程:',os.getpid())
    print(f'子进程的父进程:',os.getppid())

    while True:
        time.sleep(1)
        print(f'子进程:', os.getpid())
        print(f'子进程的父进程:', os.getppid())
        print('-'*30)

    print('子进程结束')

if __name__ == '__main__':
    print(f'主进程的父进程ID号:{os.getppid()}')
    print(f'主进程的进程ID号:{os.getpid()}')
    #创建一个进程,target:新创建的这个进程会去执行func这个函数
    p1 = Process(target=func,args=(os.getpid(),))

    #启动进程
    p1.start()

    print('-'*30)
    time.sleep(10)
    p1.terminate()  # 结束子进程
    time.sleep(3)
    print('子进程状态:',p1.is_alive())
    print('主进程中-子进程id:', p1.pid)

    print('主进程结束')

3:进程间通信与同步

进程之间是相互独立的,每个进程都有自己的地址空间,代码段、堆栈段和数据段。其主要通过以下方法来进行进程之间的通信。

3.1:管道Pipe

# -*- coding: utf-8 -*-

import os
import time
from multiprocessing import Process,Pipe

def func1(conn):
    while True:
        # time.sleep(10000)  # 这里卡住,来测试conn.send(i) 是不是阻塞
        msg = conn.recv()  #  没有数据了的时候,会阻塞在这里,
        print(f">>{msg}")
        # conn.send(1000)  # 收发在不同的进程中进行,不然会卡住。
        if msg == "exit":  #
            break

        # time.sleep(2)

# 产生数据,向管道中写
def func2(conn):
    i = 0
    while True:
        # print(f'in func2 {i}')   # 打印日志,来测试是否阻塞
        conn.send(i)  #  如果只是向管道中写,达到一定数量(我机子上是1408)就会阻塞
        i += 1

        if i > 10000:
            break

def main():
    send, recv = Pipe()

    p_list = []
    for i in range(5):  # 创建5个进程来读数据
        p1 = Process(target=func1, args=(recv,))
        p1.start()
        p_list.append(p1)

    p2 = Process(target=func2, args=(send,))  # 产生数据,或者叫做“生产者”
    p2.start()

    p2.join()
    print('p2.join() 之后')
    # 执行到这里,说明产生数据的进程,已结束。
    # 给各个读进程发送结束命令
    for i in range(5):
        send.send('exit')

    for p in p_list:
        p.join()

    print("主进程结束")


if __name__ == '__main__':
    main()


3.2:队列Queue

# -*- coding: utf-8 -*-

import os
import time
from multiprocessing import Process,Queue

def func1(q):
    while True:
        msg = q.get()  #  当 q.empty() 为True(即队列为空)的时候,get会阻塞
        print(f">>{msg}")
        if msg == "exit":  #
            break

        # time.sleep(2)

# 产生数据
def func2(q):
    i = 0
    while True:
        # stru_t = time.localtime()
        # strTime = time.strftime('%Y-%m-%d %H:%M:%S', stru_t)
        q.put(i)  # 当 q.full() 为True(即队列已满时会阻塞)
        i += 1

        if i > 10000:
            break

def main():
    # 队列大小为5,当q中的数据达到5条时,再执行q.put()操作会阻塞
    # 队列为空的时,执行q.get()操作会阻塞
    q = Queue(5)

    p_list = []
    for i in range(5):  # 创建5个进程来读数据
        p1 = Process(target=func1, args=(q,))
        p1.start()
        p_list.append(p1)

    p2 = Process(target=func2, args=(q,))  # 产生数据,或者叫做“生产者”
    p2.start()

    p2.join()
    # 执行到这里,说明产生数据的进程已结束。
    # 给各个读进程发送结束命令
    for i in range(5):
        q.put('exit')

    for p in p_list:
        p.join()

    print("主进程结束")

if __name__ == '__main__':
    main()

3.3:队列JoinableQueue

# -*- coding: utf-8 -*-

import os
import time
import traceback
from multiprocessing import Process,JoinableQueue

#
def func1(q):
    while True:
        try:
            msg = q.get()  # msg = q.get(False) 当q为空时会抛出异常
            print(f">>{msg}")
            q.task_done()  # 处理完成数据,需要给q对象发送了一个任务结束的信号
        except Exception:
            val = traceback.format_exc()
            print(val)
            break

        # time.sleep(2)

# 产生数据:
def func2(q):
    i = 0
    while True:
        q.put(i)  #
        i += 1

        if i > 10000:
            break

    q.join() #也可以放在这里

def main():
    q = JoinableQueue(5)

    p_list = []
    for i in range(5):  # 创建5个进程来读数据
        p1 = Process(target=func1, args=(q,))
        p1.daemon = True  #  daemon 设置为 True时,父进程结束时,子进程结束也会结束。,
        p1.start()
        p_list.append(p1)

    p2 = Process(target=func2, args=(q,))  # 产生数据,或者叫做“生产者”
    p2.start()
    p2.join()  # 这里需要等待产生数据的进程结束

    #
    # q.join() # 等待队列中的数据处理完,不需要像之前那样发送"exit"来结束进程;也可以放到func2中去。

    print("主进程结束")

if __name__ == '__main__':
    main()

3.4:共享数据:Managers

各个进程之间是相互独立的,进程之间的数据也是独立的,不能共享。python3中提供了Managers模块来实现各个进程之间数据的共享。

# -*- coding: utf-8 -*-

import os
import time
import traceback
from multiprocessing import Process,Manager

#
def func1(dic,lst,index,dic2):
    # 观察 dic,lst,dic2 的内存地址,在子进程中与在父进程中都不同;
    print(f'子进程{index},id(dic):{id(dic)},id(dic2):{id(dic2)},id(lst):{id(lst)}')
    dic[index] = os.getpid() # dic是由Manager创建的,显然每个进程中的地址都不同,但其中的值是共享的,在每个进程中都能访问,修改;
    dic2[index] = os.getpid() # 只有在自己进程中可以访问

    while True:
        try:
            s = lst.pop()
            print(f'进程{index},取出:{s}: 剩下lst={lst}')
            if len(lst) <= 0:
                break
            time.sleep(1)
        except:
            break

    print(f'子进程{index}结束:dic:{dic},dic2:{dic2},lst:{lst}')

def main():
    with Manager() as manager:
        dic = manager.dict()  # 通过manager创建的数据才能在进程间共享,操作其创建的变量不需要加锁,Manager已默认加锁。
        lst = manager.list(range(20))
        dic2 = {} #  这种方式创建的变量,只能是在本进程中使用,
        print(f'主进程:id(dic):{id(dic)},id(dic2):{id(dic2)},id(lst):{id(lst)}')

        p_list = []
        for index in range(5):  # 创建5个进程来读数据
            p1 = Process(target=func1, args=(dic,lst,index,dic2,))
            p1.start()
            p_list.append(p1)


        for p in p_list:
            p.join()  # 等待子进程结束

        # 打印结果
        print("打印结果:")
        print(dic)
        print(dic2)
        print(lst)
        print(f'主进程结束:id(dic):{id(dic)},id(dic2):{id(dic2)},id(lst):{id(lst)}')

    print("主进程结束")


if __name__ == '__main__':
    main()


3.5:信号量:Semaphore

# -*- coding: utf-8 -*-


import time
import random
from multiprocessing import Process,Semaphore,Manager



def func1(index,s,lst,dic):
    # print(lst)
    while True:
        try:
            s.acquire()  # 每次acquire就减1,直到减到0,为0时就等待
            val = lst.pop()
            s.release()
            dic['cnt'] += 1
            print(f'{dic["cnt"]},进程{index},取出:{val}: 剩下lst={lst}')
            if len(lst) <= 0:
                break
            # time.sleep(1)
        except:
            s.release() # 释放
            break

def main():
    s = Semaphore(5)  # 创建一个计数器,同时只能5个进程在运行
    with Manager() as manager:
        # print(help(manager))
        # print(help(Manager))
        lst = manager.list(range(20))
        dic = manager.dict()
        dic['cnt'] = 0   # 使用共享变量来计数,manager创建的变量不需要加锁,Manager已默认加锁。
        print(dic['cnt'] )

        p_list = []
        for i in range(10):
            p1 = Process(target=func1, args=(i, s, lst,dic,))
            p1.start()
            p_list.append(p1)

        for p in p_list:
            p.join()  # 等待子进程结束

        print(dic['cnt'])


if __name__ == '__main__':
    main()

3.6:事件:Event

# -*- coding: utf-8 -*-
import time
from multiprocessing import Process,Event

def light(e):
    while 1:
        print('现在是红灯:')
        time.sleep(5)
        e.set()   # 设置event的状态值为True ;
        print('现在是绿灯:')
        time.sleep(3)
        e.clear()  # 恢复event的状态值为False。

def car(index,e):
    if e.is_set():  # 返回event的状态值;
        # 状态为True
        print(f'    现在是绿灯,{index}:过马路中!')
    else:
        print(f'    现在是红灯{index}:等待中!')
        e.wait()  #  如果 event.isSet()==False将阻塞
        print(f'    红灯变绿灯,{index}:可以走了!')

if __name__ == '__main__':
    e = Event()
    lgh = Process(target=light,args=(e,))
    lgh.start()
    cnt = 0

    while 1:
        time.sleep(1) # 每隔1秒来一辆car
        p1 = Process(target=car,args=(cnt,e,))
        p1.start()
        cnt += 1

3.7:锁:Lock

锁是为了实现数据的唯一性,即同时只能由一个进程对其操作;

# -*- coding: utf-8 -*-

import json
import time
import  random

from multiprocessing import Process,Lock

def get_ticket(index,ticket_lock):
    ticket_lock.acquire()  # 获取锁,如果被其他进程获得,此处将会阻塞,
    with open('ticket','r' ) as f:
        last_ticket_info = json.load(f)

    last_ticket = last_ticket_info['count']
    if last_ticket > 0:
        print(f'{index}号进程,抢到票,抢到{last_ticket}号票')
        time.sleep(random.random())
        last_ticket -= 1
        last_ticket_info['count'] =  last_ticket
        with open('ticket','w') as f:
            json.dump(last_ticket_info, f)
    else:
        print(f'{index}号进程,没有票了,明天再来')

    ticket_lock.release() # 释放锁 

def main():
    ticket_lock = Lock()
    for i in range(10):
        p = Process(target=get_ticket, args=(i, ticket_lock))
        p.start()

if __name__ == '__main__':
    main()

"""
ticket文件中的内容为:

{"count": 5}
"""

把get_ticket函数中的下列语句注释,再运行一次:
ticket_lock.acquire() 
ticket_lock.release()

3.8:进程池:Pool

进程池就是先创建一些进程放在那,当有处理任务来的时候,就去进程池中获取一个进程来处理这个任务,在Python3中提供了Pool模块来处理进程池的相关操作,不需要我们自己来实现进程池;

进程池中有两个方法:
apply:同步,一般不使用
apply_async:异步
 

# -*- coding: utf-8 -*-

from  multiprocessing import Process,Pool
import os, time, random

def fun1(index):
    print(f"开始处理[{index}]-进程{os.getpid()}.")
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print(f"结束处理[{index}]-进程{os.getpid()},处理时间:{end - start}.")

def gen_data(pool):
    cnt = 0;
    while  True:
        pool.apply_async(func=fun1, args=(cnt,))
        cnt += 1
        # time.sleep(1)
        if cnt > 100:
            break

def main():
    pool = Pool(6)  # 创建一个6个进程的进程池
    gen_data(pool)

    pool.close() # close表示不能添加处理任务
    pool.join()  #  等待进程池处理完所有任务

if __name__=='__main__':
    main()
    print('主进程结束')

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要使用Python构建高并发高扩展的学习系统,您可以考虑使用以下技术和框架: 1. 异步编程:使用asyncio库进行异步编程,以便同时处理多个并发请求。 2. 分布式任务队列:使用Celery或RabbitMQ等任务队列,将任务分发到多个工作节点,以便实现水平扩展。 3. 缓存系统:使用Redis或Memcached等缓存系统,以便缓存频繁请求的数据,减少对数据库的访问次数。 4. Web框架:使用Django或Flask等Web框架,以便快速搭建Web服务。 5. 数据库:使用MySQL或PostgreSQL等关系型数据库,以便存储和管理系统的数据。 6. 消息中间件:使用Kafka或ActiveMQ等消息中间件,以便异步处理系统中的事件和消息。 通过上述技术和框架的结合使用,您可以构建一个高并发高扩展的学习系统。 ### 回答2: 要实现高并发和高扩展的学习系统,可以按照以下方法利用Python: 1. 使用异步编程:Python提供了诸如asyncio和aiohttp等库,可用于实现异步编程。通过使用异步I/O和协程,可以提高系统的并发处理能力。 2. 使用分布式架构:将系统拆分成多个独立的服务模块,并将其部署在不同的服务器上。可以使用Python的一些开源分布式框架,如Celery或Pyro等来管理任务分发和节点通信,以实现系统的横向扩展能力。 3. 使用缓存和消息队列:将频繁读取的数据进行缓存,例如使用Redis作为缓存数据库。另外,可以使用消息队列(如RabbitMQ或Kafka)进行解耦和异步处理,以避免系统阻塞。 4. 使用负载均衡:通过将流量分发到不同的服务器上,可以实现请求负载的均衡,提高系统的并发能力。可以使用Python实现的一些负载均衡器,如HAProxy或Nginx。 5. 使用高性能的数据库:选择适合并发场景的高性能数据库,如MongoDB或Elasticsearch。这些数据库具有良好的横向扩展性,可以在处理大量数据时提供更好的性能。 6. 使用监控和日志工具:为了追踪系统运行情况并进行性能优化,可以使用Python提供的监控和日志工具,如Prometheus和ELK(Elasticsearch + Logstash + Kibana)。这些工具可以帮助您定位系统的瓶颈和问题,并优化系统性能。 总体而言,使用Python编程语言可以轻松地实现高并发和高扩展的学习系统。通过合理选择和应用相关的库和工具,可以提高系统的并发处理能力,支持大量用户的同时访问,并实现系统的横向扩展。 ### 回答3: 要实现一个高并发和高扩展的学习系统,可以使用Python编程语言,并采用以下几个方法: 1. 使用异步编程: Python提供了多个异步框架(如asyncio和Tornado),可以通过使用异步编程技术来处理并发请求。异步编程可以提高系统的吞吐量,同时避免阻塞线程的情况。 2. 使用分布式架构: 使用Python的分布式系统框架(例如Celery或Pyro),可以将任务分发到多个节点或服务器上进行处理。这样能够提高系统的扩展性,分担服务器的负载,以应对大量并发请求。 3. 数据库优化: 对于学习系统来说,数据库是一个重要的组成部分。使用高性能的数据库技术,如NoSQL(如MongoDB)或数据缓存(如Redis),可以加快数据库的读写速度,提高系统的并发处理能力。 4. 使用缓存: 在学习系统中,有些数据是可以被缓存的,如用户信息、用户学习的课程等。使用缓存技术,如Memcached或Redis,可以将经常访问的数据存储在内存中,减少对数据库的访问,提高系统的响应速度和并发能力。 5. 水平扩展: 当系统的负载逐渐增加时,可以考虑将系统进行水平扩展。使用负载均衡器,将请求分发给多个服务器处理,可以提高系统的并发处理能力,并保持系统的可用性。 6. 异常处理和错误日志: 在高并发系统中,出现异常和错误是不可避免的。使用适当的异常处理机制,并记录详细的错误日志,有助于排查问题和改进系统性能。 总之,使用Python编程语言可以通过异步编程、分布式架构、数据库优化、缓存、水平扩展等技术手段来实现高并发和高扩展的学习系统。这些方法可以提高系统的并发处理能力、响应速度和吞吐量,以满足大量用户同时访问的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值