Flask 多进程多线程异步非阻塞测试

多进程

1. multiprocessing.Process

from flask import Flask
import time
import multiprocessing

app = Flask(__name__)

num = multiprocessing.Value("d", 1)  # d表示数值,主进程与子进程共享这个value。(主进程与子进程都是用的同一个value)

def func(num):

 	# 测试是否异常,有异常直接结束子进程
    # print('子线程值:', num.value)
    # if 1:
    #     num.value = 1  # 子进程改变数值的值,主进程跟着改变
    #     print('重置num值:', num.value)
    #     quit('有异常退出')

    print('子线程1:', num.value)
    time.sleep(5)
    print('睡眠完成')
    a=1/0
    num.value = 1  # 子进程改变数值的值,主进程跟着改变
    print('子线程2:',num.value)
    # return '子线程处理完成'


@app.route('/')
def hello_world():

    if num.value:
        num.value=0
        p = multiprocessing.Process(target=func, args=(num,))
        p.start()

        print('提交成功,请等待处理')
        return '提交成功,请等待处理'

    if not num.value:
        print('正在执行,请等待')
        return '正在执行,请等待'

    return 'Hello World!'


if __name__ == '__main__':
    app.run(debug=True, port=6006)

2、pool

from multiprocessing import Pool,Manager
import os, time, random
 
manager=Manager()
value1=manager.Value('i',1)
 
def work1(msg):
    print('值1:',msg.value)
    time.sleep(5)  #将数据交给,处理程序
    print('睡眠完成')
    # return {"a":a,"b":b,"c":c}
    # print("循环任务%d由进程号%d进程执行" % (msg, os.getpid()))
    # time.sleep(random.random())  # 随机生产0-1的浮点数
    # end_time = time.time()  # 结束时间
    # print(msg, "执行完毕,耗时%0.2f" % (end_time - start_time))
    msg.value=1
    print('值2:',msg.value)
 
 
@app.route('/')
def work():
    if value1.value:
        value1.value=0
        print('子进程开始执行')
        pool = Pool(3)  # 定义一个进程池,最大进程数为3
        pool.apply_async(func=work1, args=(value1,))  
        pool.close()  # 关闭进程池,关闭后pool不再接收新的请求任务
        return '子进程开始执行!'
 
    else:
        print('有任务正在执行')
        return '有任务正在执行

3.torch.multiprocessing

def func(a,b,c):
    time.sleep(5)  #将数据交给,处理程序
    print('睡眠完成')
    return {
    
    "a":a,"b":b,"c":c}
 
@app.route('/')
def func2():
    

    ctx = torch.multiprocessing.get_context("spawn")
    print(torch.multiprocessing.cpu_count())
    pool = ctx.Pool(5) # 7.7G

    i=100
    pool.apply_async(func, args=(f'a{
      
      i}',f'b{
      
      i}',f'c{
      
      i}'))
    pool.close() 
    # i=100
    # pool.apply_async(func, args=(f'a{i}',f'b{i}',f'c{i}'))



    pool_list = [1,2,3]

    for i in range(0,5):
        pool.apply_async(func, args=(f'a{
      
      i}',f'b{
      
      i}',f'c{
      
      i}'))
        # print('zhi:',res.get())
        # pool_list.append(res)

    pool.close()  # 关闭进程池,不再接受新的进程
    # pool.join()  # 主进程阻塞等待子进程的退出
    
    for i in pool_list:
        # data = i.get()
        print('数据:',i)

    return 'Hello World!'



spawn在pool中可以,process直接卡住

   # ctx = torch.multiprocessing
            ctx = torch.multiprocessing.get_context("spawn")
            print(torch.multiprocessing.cpu_count())
            pool = ctx.Pool(2) # 7.7G
            num=1
            pool.apply_async(taskModel_execute, args=(start_model, execut_data),error_callback=throw_error)
            pool.close() 
 ctx = torch.multiprocessing.get_context("spawn")
p = ctx.Process(target=taskModel_execute, args=(start_model, execut_data))
 p.start()

进程共享变量

Value、Array是通过共享内存的方式共享数据
Manager是通过共享进程的方式共享数据。
'spawn’开启多进程程数据拷贝

 	num=multiprocessing.Value('d',1.0)#num=0
    arr=multiprocessing.Array('i',range(10))#arr=range(10)
    p=multiprocessing.Process(target=func1,args=(num,arr))
	manager=Manager()
    list1=manager.list([1,2,3,4,5])
    dict1=manager.dict()
    array1=manager.Array('i',range(10))
    value1=manager.Value('i',1)

进程间共享变量
如果希望不同进程读写同一个变量,需要做特殊的声明。multiprocessing提供了两种实现方式,一种是共享内存,一种是使用服务进程。共享内存只支持两种数据结构Value和Array。
子进程和主进程访问的count在内存中的地址是相同的。这里有两点需要注意:

  • 1.multiprocessing.Value对象和Process一起使用的时候,可以像上面那样作为全局变量使用,也可以作为传入参数使用。但是和Pool一起使用的时候,只能作为全局变量使用,作为传入参数使用会报错。 RuntimeError: Synchronized objects should only be shared between processes through inheritance
  • 2.多个进程读写共享变量的时候,要注意操作是否是进程安全的。对于前面的累加计数器,虽然是一个语句,但是涉及到读和写,和进程的局域临时变量,这个操作不是进程安全的。多进程的累加的时候,会出现不正确的结果。需要给cls.count += 1加上锁。加锁的方式,可以使用外部的锁,也可以直接使用get_lock()方法。
  • 3.共享内存支持的数据结构有限,另一种共享变量的方式是使用服务进程管理需要共享的变量,其他进程操作共享变量的时候通过和服务进程的交互实现。这种方式支持列表、字典等类型,而且可以实现多台机器之间共享变量。但是速度要比共享内存的方式慢。另外,这种方式可以用作Pool的传入参数。同样的,对于非进程安全的操作,也需要加锁。

4、进程异常捕获(非阻塞)

1.multiprocessing.Process捕获不了异常

p = multiprocessing.Process(target=taskimg_execute, args=(start_img,))
p.start()

2.pool捕获不了,只能通过函数接收

def throw_error(e):
    print("error回调方法: ", e)
    return {
    
    'code': 440, 'msg': '进程创建异常'+str(e), 'data': {
    
    'status': 400}}

pool = multiprocessing.Pool(processes = 3)
pool.apply_async(taskimg_execute, (start_img,),rror_callback=throw_error)  pool.close()

多线程

import threading
from flask import Flask
import time

app = Flask(__name__)
g_num = 1

def func(data):
    global g_num

    print('子线程1:', g_num)
    time.sleep(5)
    print('睡眠完成')
    # a=1/0
    g_num = 1  # 子进程改变数值的值,主进程跟着改变
    print('子线程2:',g_num)
    # return '子线程处理完成'

@app.route('/')
def hello_world():
    global g_num

    print('主进程:',g_num)

    if g_num:
        g_num=0
        # t1 = threading.Thread(target=func)
        t1 = threading.Thread(target=func, args=(6,))
        t1.start()
        # time.sleep(1)
        print('提交成功,请等待处理')
        return '提交成功,请等待处理'

    if not g_num:
        print('正在执行,请等待')
        return '正在执行,请等待'

    return 'Hello World!'


if __name__ == '__main__':
    app.run(debug=True, port=6006)

线程中开启进程

import threading
from flask import Flask
import time
import multiprocessing

app = Flask(__name__)
g_num = 1



def func2(num):

    print('进程1:', num.value)
    time.sleep(2)
    print('睡眠完成')
    # a=1/0
    # num.value = 1  # 子进程改变数值的值,主进程跟着改变

    print('这是子进程处理')
    print('进程2:',num.value)
    # return '子线程处理完成'



def func(data):
    global g_num

    print('子线程1:', g_num)
    # time.sleep(5)
    # print('睡眠完成')

    num = multiprocessing.Value("d", 1)

    p = multiprocessing.Process(target=func2, args=(num,))
    p.start()

    print('子线程提交任务')


    # a=1/0
    g_num = 1  # 子进程改变数值的值,主进程跟着改变
    print('子线程2:',g_num)
    # return '子线程处理完成'

@app.route('/')
def hello_world():
    global g_num

    print('主进程:',g_num)

    if g_num:
        g_num=0
        # t1 = threading.Thread(target=func)
        t1 = threading.Thread(target=func, args=(6,))
        t1.start()
        # time.sleep(1)
        print('提交成功,请等待处理')
        return '提交成功,请等待处理'

    if not g_num:
        print('正在执行,请等待')
        return '正在执行,请等待'

    return 'Hello World!'

@app.route('/thread')
def mytest():
    from threading import Thread
    from time import sleep, ctime

    def funcx(name, sec):
        print('---开始---', name, '时间', ctime())
        # sleep(sec)
        time.sleep(3)
        print('***结束***', name, '时间', ctime())

    # 创建 Thread 实例
    t1 = Thread(target=funcx, args=('第一个线程', 3))
    t2 = Thread(target=funcx, args=('第二个线程', 2))
    t3 = Thread(target=funcx, args=('第一个线程',3))
    t4 = Thread(target=funcx, args=('第二个线程', 2))

    # 启动线程运行
    t1.start()
    t2.start()
    t3.start()
    t4.start()

    return 'i am a thread test!'





if __name__ == '__main__':
    app.run(debug=True, port=6006)


可以在任务管理器中查看,进程的各个数
在这里插入图片描述

多进程spawn、fork、forkserver (多进程模型训练)

raise ValueError(‘参数错误’)抛出的异常可以捕获

Python 多进程编程:创建进程的三种模式之spawn、fork、forkserver
首先fork和spawn都是构建子进程的不同方式,区别在于:
fork:除了必要的启动资源外,其他变量,包,数据等都继承自父进程,并且是copy-on-write的,也就是共享了父进程的一些内存页,因此启动较快,但是由于大部分都用的父进程数据,所以是不安全的进程
spawn:从头构建一个子进程,父进程的数据等拷贝到子进程空间内,拥有自己的Python解释器,所以需要重新加载一遍父进程的包,因此启动较慢,由于数据都是自己的,安全性较高

扫描二维码关注公众号,回复: 17015385 查看本文章

raise ValueError(‘参数错误’)抛出的异常可以捕获
多进程调用训练模型
报错:Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, you must use the ‘spawn’ start method

import torch
torch.multiprocessing.set_start_method('spawn',force=True)
#解决不了
# print(multiprocessing.get_start_method())
# multiprocessing.set_start_method('spawn', force=True)

try:
        print('subprocess执行命令')
        core.testtrain('我是主线程')
        print('sub执行结束,没有异常')
    except Exception as e:
        print("Couldn't parse")
        print('捕获异常Reason:', e)
        

猜你喜欢

转载自blog.csdn.net/weixin_44986037/article/details/132384028
今日推荐