python 协程池和pool.map用法

一、问题描述

现在有一段代码,需要扫描一个网段内的ip地址,是否可以ping通。

执行起来效率太慢,需要使用协程。

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import time
import signal
import subprocess
import gevent
import gevent.pool
from gevent import monkey;monkey.patch_all()


def custom_print(content,colour='white'):
    """
    写入日志文件
    :param content: 内容
    :param colour: 颜色
    :return: None
    """
    # 颜色代码
    colour_dict = {
        'red': 31,  # 红色
        'green': 32,  # 绿色
        'yellow': 33,  # 黄色
        'blue': 34,  # 蓝色
        'purple_red': 35,  # 紫红色
        'bluish_blue': 36, # 浅蓝色
        'white': 37,  # 白色
    }
    choice = colour_dict.get(colour)  # 选择颜色

    info = "\033[1;{};1m{}\033[0m".format(choice, content)
    print(info)


def execute_linux2(cmd, timeout=10, skip=False):
    """
    执行linux命令,返回list
    :param cmd: linux命令
    :param timeout: 超时时间,生产环境, 特别卡, 因此要3秒
    :param skip: 是否跳过超时限制
    :return: list
    """
    p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE,shell=True,close_fds=True,preexec_fn=os.setsid)

    t_beginning = time.time()  # 开始时间
    while True:
        if p.poll() is not None:
            break
        seconds_passed = time.time() - t_beginning
        if not skip:
            if seconds_passed > timeout:
                # p.terminate()
                # p.kill()
                # raise TimeoutError(cmd, timeout)
                custom_print('错误, 命令: {},本地执行超时!'.format(cmd),"red")
                # 当shell=True时,只有os.killpg才能kill子进程
                try:
                    # time.sleep(1)
                    os.killpg(p.pid, signal.SIGUSR1)
                except Exception as e:
                    pass
                return False

    result = p.stdout.readlines()  # 结果输出列表
    return result


class NetworkTest(object):
    def __init__(self):
        self.flag_list = []

    def check_ping(self,ip):
        """
        检查ping
        :param ip: ip地址
        :return: none
        """
        cmd = "ping %s -c 2" % ip
        # print(cmd)
        # 本机执行命令
        res = execute_linux2(cmd,2)
        # print(res)
        if not res:
            custom_print("错误, 执行命令: {} 失败".format(cmd), "red")
            self.flag_list.append(False)
            return False

        res.pop()  # 删除最后一个元素
        last_row = res.pop().decode('utf-8').strip()  # 再次获取最后一行结果
        if not last_row:
            custom_print("错误,执行命令: {} 异常","red")
            self.flag_list.append(False)
            return False

        res = last_row.split()  # 切割结果
        # print(res,type(res),len(res))
        if len(res) <10:
            custom_print("错误,切割 ping 结果异常","red")
            self.flag_list.append(False)
            return False

        if res[5] == "0%":  # 判断丢包率
            custom_print("正常, ip: {} ping正常 丢包率0%".format(ip), "green")
        else:
            self.flag_list.append(False)
            custom_print("错误, ip: {} ping异常 丢包率100%".format(ip), "red")

    def main(self):
        """
        主程序
        :return:
        """
        for num in range(1, 256):
            ip = '192.168.10.{}'.format(num)
            self.check_ping(ip)


if __name__ == '__main__':
    startime = time.time()  # 开始时间

    NetworkTest().main()

    endtime = time.time()
    take_time = endtime - startime

    if take_time < 1:  # 判断不足1秒时
        take_time = 1  # 设置为1秒
    # 计算花费时间
    m, s = divmod(take_time, 60)
    h, m = divmod(m, 60)

    custom_print("本次花费时间 %02d:%02d:%02d" % (h, m, s),"green")
View Code

改造成,协程执行。

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import time
import signal
import subprocess
import gevent
import gevent.pool
from gevent import monkey;monkey.patch_all()


def custom_print(content,colour='white'):
    """
    写入日志文件
    :param content: 内容
    :param colour: 颜色
    :return: None
    """
    # 颜色代码
    colour_dict = {
        'red': 31,  # 红色
        'green': 32,  # 绿色
        'yellow': 33,  # 黄色
        'blue': 34,  # 蓝色
        'purple_red': 35,  # 紫红色
        'bluish_blue': 36, # 浅蓝色
        'white': 37,  # 白色
    }
    choice = colour_dict.get(colour)  # 选择颜色

    info = "\033[1;{};1m{}\033[0m".format(choice, content)
    print(info)


def execute_linux2(cmd, timeout=10, skip=False):
    """
    执行linux命令,返回list
    :param cmd: linux命令
    :param timeout: 超时时间,生产环境, 特别卡, 因此要3秒
    :param skip: 是否跳过超时限制
    :return: list
    """
    p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE,shell=True,close_fds=True,preexec_fn=os.setsid)

    t_beginning = time.time()  # 开始时间
    while True:
        if p.poll() is not None:
            break
        seconds_passed = time.time() - t_beginning
        if not skip:
            if seconds_passed > timeout:
                # p.terminate()
                # p.kill()
                # raise TimeoutError(cmd, timeout)
                custom_print('错误, 命令: {},本地执行超时!'.format(cmd),"red")
                # 当shell=True时,只有os.killpg才能kill子进程
                try:
                    # time.sleep(1)
                    os.killpg(p.pid, signal.SIGUSR1)
                except Exception as e:
                    pass
                return False

    result = p.stdout.readlines()  # 结果输出列表
    return result


class NetworkTest(object):
    def __init__(self):
        self.flag_list = []

    def check_ping(self,ip):
        """
        检查ping
        :param ip: ip地址
        :return: none
        """
        cmd = "ping %s -c 2" % ip
        # print(cmd)
        # 本机执行命令
        res = execute_linux2(cmd,2)
        # print(res)
        if not res:
            custom_print("错误, 执行命令: {} 失败".format(cmd), "red")
            self.flag_list.append(False)
            return False

        res.pop()  # 删除最后一个元素
        last_row = res.pop().decode('utf-8').strip()  # 再次获取最后一行结果
        if not last_row:
            custom_print("错误,执行命令: {} 异常","red")
            self.flag_list.append(False)
            return False

        res = last_row.split()  # 切割结果
        # print(res,type(res),len(res))
        if len(res) <10:
            custom_print("错误,切割 ping 结果异常","red")
            self.flag_list.append(False)
            return False

        if res[5] == "0%":  # 判断丢包率
            custom_print("正常, ip: {} ping正常 丢包率0%".format(ip), "green")
        else:
            self.flag_list.append(False)
            custom_print("错误, ip: {} ping异常 丢包率100%".format(ip), "red")

    def main(self):
        """
        主程序
        :return:
        """
        process_list = []
        for num in range(1, 256):
            ip = '192.168.10.{}'.format(num)
            # self.check_ping(ip)
            # 将任务加到列表中
            process_list.append(gevent.spawn(self.check_ping, ip))

        gevent.joinall(process_list)  # 等待所有协程结束


if __name__ == '__main__':
    startime = time.time()  # 开始时间

    NetworkTest().main()

    endtime = time.time()
    take_time = endtime - startime

    if take_time < 1:  # 判断不足1秒时
        take_time = 1  # 设置为1秒
    # 计算花费时间
    m, s = divmod(take_time, 60)
    h, m = divmod(m, 60)

    custom_print("本次花费时间 %02d:%02d:%02d" % (h, m, s),"green")
View Code

执行输出:

...
错误, 命令: ping 192.168.10.250 -c 2,本地执行超时!
错误, 执行命令: ping 192.168.10.250 -c 2 失败
错误, 命令: ping 192.168.10.255 -c 2,本地执行超时!
错误, 执行命令: ping 192.168.10.255 -c 2 失败
本次花费时间 00:00:07

注意:切勿在windows系统中运行,否则会报错

AttributeError: module 'os' has no attribute 'setsid'

二、使用协程池

上面直接将所有任务加到列表中,然后一次性,全部异步执行。那么同一时刻,最多有多少任务执行呢?

不知道,可能有256个吧?

注意:如果这个一个很耗CPU的程序,可能会导致服务器,直接卡死。

那么,我们应该要限制它的并发数。这个时候,需要使用协程池,固定并发数。

比如:固定为100个

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import time
import signal
import subprocess
import gevent
import gevent.pool
from gevent import monkey;monkey.patch_all()


def custom_print(content,colour='white'):
    """
    写入日志文件
    :param content: 内容
    :param colour: 颜色
    :return: None
    """
    # 颜色代码
    colour_dict = {
        'red': 31,  # 红色
        'green': 32,  # 绿色
        'yellow': 33,  # 黄色
        'blue': 34,  # 蓝色
        'purple_red': 35,  # 紫红色
        'bluish_blue': 36, # 浅蓝色
        'white': 37,  # 白色
    }
    choice = colour_dict.get(colour)  # 选择颜色

    info = "\033[1;{};1m{}\033[0m".format(choice, content)
    print(info)


def execute_linux2(cmd, timeout=10, skip=False):
    """
    执行linux命令,返回list
    :param cmd: linux命令
    :param timeout: 超时时间,生产环境, 特别卡, 因此要3秒
    :param skip: 是否跳过超时限制
    :return: list
    """
    p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE,shell=True,close_fds=True,preexec_fn=os.setsid)

    t_beginning = time.time()  # 开始时间
    while True:
        if p.poll() is not None:
            break
        seconds_passed = time.time() - t_beginning
        if not skip:
            if seconds_passed > timeout:
                # p.terminate()
                # p.kill()
                # raise TimeoutError(cmd, timeout)
                custom_print('错误, 命令: {},本地执行超时!'.format(cmd),"red")
                # 当shell=True时,只有os.killpg才能kill子进程
                try:
                    # time.sleep(1)
                    os.killpg(p.pid, signal.SIGUSR1)
                except Exception as e:
                    pass
                return False

    result = p.stdout.readlines()  # 结果输出列表
    return result


class NetworkTest(object):
    def __init__(self):
        self.flag_list = []

    def check_ping(self,ip):
        """
        检查ping
        :param ip: ip地址
        :return: none
        """
        cmd = "ping %s -c 2" % ip
        # print(cmd)
        # 本机执行命令
        res = execute_linux2(cmd,2)
        # print(res)
        if not res:
            custom_print("错误, 执行命令: {} 失败".format(cmd), "red")
            self.flag_list.append(False)
            return False

        res.pop()  # 删除最后一个元素
        last_row = res.pop().decode('utf-8').strip()  # 再次获取最后一行结果
        if not last_row:
            custom_print("错误,执行命令: {} 异常","red")
            self.flag_list.append(False)
            return False

        res = last_row.split()  # 切割结果
        # print(res,type(res),len(res))
        if len(res) <10:
            custom_print("错误,切割 ping 结果异常","red")
            self.flag_list.append(False)
            return False

        if res[5] == "0%":  # 判断丢包率
            custom_print("正常, ip: {} ping正常 丢包率0%".format(ip), "green")
        else:
            self.flag_list.append(False)
            custom_print("错误, ip: {} ping异常 丢包率100%".format(ip), "red")

    def main(self):
        """
        主程序
        :return:
        """
        process_list = []
        pool= gevent.pool.Pool(100)  # 协程池固定为100个
        for num in range(1, 256):
            ip = '192.168.10.{}'.format(num)
            # self.check_ping(ip)
            # 将任务加到列表中
            process_list.append(pool.spawn(self.check_ping, ip))

        gevent.joinall(process_list)  # 等待所有协程结束


if __name__ == '__main__':
    startime = time.time()  # 开始时间

    NetworkTest().main()

    endtime = time.time()
    take_time = endtime - startime

    if take_time < 1:  # 判断不足1秒时
        take_time = 1  # 设置为1秒
    # 计算花费时间
    m, s = divmod(take_time, 60)
    h, m = divmod(m, 60)

    custom_print("本次花费时间 %02d:%02d:%02d" % (h, m, s),"green")
View Code

再次执行,效果如下:

...
错误, 执行命令: ping 192.168.10.254 -c 2 失败
错误, 命令: ping 192.168.10.255 -c 2,本地执行超时!
错误, 执行命令: ping 192.168.10.255 -c 2 失败
本次花费时间 00:00:15

可以,发现花费的时间,明显要比上面慢了!

pool.map 单个参数

其实,还有一种写法,使用pool.map,语法如下:

pool.map(func,iterator)

比如:

pool.map(self.get_kernel, NODE_LIST)

注意:func是一个方法,iterator是一个迭代器。比如:list就是一个迭代器

使用map时,func只能接收一个参数。这个参数就是,遍历迭代器的每一个值。

使用map,完整代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import time
import signal
import subprocess
import gevent
import gevent.pool
from gevent import monkey;monkey.patch_all()


def custom_print(content,colour='white'):
    """
    写入日志文件
    :param content: 内容
    :param colour: 颜色
    :return: None
    """
    # 颜色代码
    colour_dict = {
        'red': 31,  # 红色
        'green': 32,  # 绿色
        'yellow': 33,  # 黄色
        'blue': 34,  # 蓝色
        'purple_red': 35,  # 紫红色
        'bluish_blue': 36, # 浅蓝色
        'white': 37,  # 白色
    }
    choice = colour_dict.get(colour)  # 选择颜色

    info = "\033[1;{};1m{}\033[0m".format(choice, content)
    print(info)


def execute_linux2(cmd, timeout=10, skip=False):
    """
    执行linux命令,返回list
    :param cmd: linux命令
    :param timeout: 超时时间,生产环境, 特别卡, 因此要3秒
    :param skip: 是否跳过超时限制
    :return: list
    """
    p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE,shell=True,close_fds=True,preexec_fn=os.setsid)

    t_beginning = time.time()  # 开始时间
    while True:
        if p.poll() is not None:
            break
        seconds_passed = time.time() - t_beginning
        if not skip:
            if seconds_passed > timeout:
                # p.terminate()
                # p.kill()
                # raise TimeoutError(cmd, timeout)
                custom_print('错误, 命令: {},本地执行超时!'.format(cmd),"red")
                # 当shell=True时,只有os.killpg才能kill子进程
                try:
                    # time.sleep(1)
                    os.killpg(p.pid, signal.SIGUSR1)
                except Exception as e:
                    pass
                return False

    result = p.stdout.readlines()  # 结果输出列表
    return result


class NetworkTest(object):
    def __init__(self):
        self.flag_list = []

    def check_ping(self,ip):
        """
        检查ping
        :param ip: ip地址
        :return: none
        """
        cmd = "ping %s -c 2" % ip
        # print(cmd)
        # 本机执行命令
        res = execute_linux2(cmd,2)
        # print(res)
        if not res:
            custom_print("错误, 执行命令: {} 失败".format(cmd), "red")
            self.flag_list.append(False)
            return False

        res.pop()  # 删除最后一个元素
        last_row = res.pop().decode('utf-8').strip()  # 再次获取最后一行结果
        if not last_row:
            custom_print("错误,执行命令: {} 异常","red")
            self.flag_list.append(False)
            return False

        res = last_row.split()  # 切割结果
        # print(res,type(res),len(res))
        if len(res) <10:
            custom_print("错误,切割 ping 结果异常","red")
            self.flag_list.append(False)
            return False

        if res[5] == "0%":  # 判断丢包率
            custom_print("正常, ip: {} ping正常 丢包率0%".format(ip), "green")
        else:
            self.flag_list.append(False)
            custom_print("错误, ip: {} ping异常 丢包率100%".format(ip), "red")

    def main(self):
        """
        主程序
        :return:
        """
        pool= gevent.pool.Pool(100)  # 协程池固定为100个
        ip_list = ["192.168.10.{}".format(i) for i in range(1, 256)]
        # 使用pool.map,语法:pool.map(func,iterator)
        pool.map(self.check_ping, ip_list)


if __name__ == '__main__':
    startime = time.time()  # 开始时间

    NetworkTest().main()

    endtime = time.time()
    take_time = endtime - startime

    if take_time < 1:  # 判断不足1秒时
        take_time = 1  # 设置为1秒
    # 计算花费时间
    m, s = divmod(take_time, 60)
    h, m = divmod(m, 60)

    custom_print("本次花费时间 %02d:%02d:%02d" % (h, m, s),"green")
View Code

注意:方法只有一个参数的情况下,使用pool.map,一行就可以搞定。这样看起来,比较精简!

pool.map 多参数

如果方法,有多个参数,需要借用偏函数实现。

完整代码如下:

#!/usr/bin/env python3
# coding: utf-8

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import time
import signal
import subprocess
import gevent
import gevent.pool
from gevent import monkey;monkey.patch_all()
from functools import partial

def custom_print(content,colour='white'):
    """
    写入日志文件
    :param content: 内容
    :param colour: 颜色
    :return: None
    """
    # 颜色代码
    colour_dict = {
        'red': 31,  # 红色
        'green': 32,  # 绿色
        'yellow': 33,  # 黄色
        'blue': 34,  # 蓝色
        'purple_red': 35,  # 紫红色
        'bluish_blue': 36, # 浅蓝色
        'white': 37,  # 白色
    }
    choice = colour_dict.get(colour)  # 选择颜色

    info = "\033[1;{};1m{}\033[0m".format(choice, content)
    print(info)


def execute_linux2(cmd, timeout=10, skip=False):
    """
    执行linux命令,返回list
    :param cmd: linux命令
    :param timeout: 超时时间,生产环境, 特别卡, 因此要3秒
    :param skip: 是否跳过超时限制
    :return: list
    """
    p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE,shell=True,close_fds=True,preexec_fn=os.setsid)

    t_beginning = time.time()  # 开始时间
    while True:
        if p.poll() is not None:
            break
        seconds_passed = time.time() - t_beginning
        if not skip:
            if seconds_passed > timeout:
                # p.terminate()
                # p.kill()
                # raise TimeoutError(cmd, timeout)
                custom_print('错误, 命令: {},本地执行超时!'.format(cmd),"red")
                # 当shell=True时,只有os.killpg才能kill子进程
                try:
                    # time.sleep(1)
                    os.killpg(p.pid, signal.SIGUSR1)
                except Exception as e:
                    pass
                return False

    result = p.stdout.readlines()  # 结果输出列表
    return result


class NetworkTest(object):
    def __init__(self):
        self.flag_list = []

    def check_ping(self,ip,timeout):
        """
        检查ping
        :param ip: ip地址
        :param ip: 超时时间
        :return: none
        """
        cmd = "ping %s -c 2 -W %s" %(ip,timeout)
        # print(cmd)
        # 本机执行命令
        res = execute_linux2(cmd,2)
        # print("res",res,"ip",ip,"len",len(res))
        if not res:
            custom_print("错误, 执行命令: {} 失败".format(cmd), "red")
            self.flag_list.append(False)
            return False

        if len(res) != 7:
            custom_print("错误,执行命令: {} 异常".format(cmd), "red")
            self.flag_list.append(False)
            return False

        res.pop()  # 删除最后一个元素
        last_row = res.pop().decode('utf-8').strip()  # 再次获取最后一行结果
        if not last_row:
            custom_print("错误,执行命令: {} 获取结果异常","red")
            self.flag_list.append(False)
            return False

        res = last_row.split()  # 切割结果
        # print(res,type(res),len(res))
        if len(res) <10:
            custom_print("错误,切割 ping 结果异常","red")
            self.flag_list.append(False)
            return False

        if res[5] == "0%":  # 判断丢包率
            custom_print("正常, ip: {} ping正常 丢包率0%".format(ip), "green")
        else:
            self.flag_list.append(False)
            custom_print("错误, ip: {} ping异常 丢包率100%".format(ip), "red")

    def main(self):
        """
        主程序
        :return:
        """
        pool= gevent.pool.Pool(100)  # 协程池固定为100个
        ip_list = ["192.168.0.{}".format(i) for i in range(1, 256)]
        # 使用协程池,执行任务。语法: pool.map(func,iterator)
        # partial使用偏函数传递参数
        # 注意:func第一个参数,必须是迭代器遍历的值。后面的参数,必须使用有命名传参
        pool.map(partial(self.check_ping, timeout=1), ip_list)


if __name__ == '__main__':
    startime = time.time()  # 开始时间

    NetworkTest().main()

    endtime = time.time()
    take_time = endtime - startime

    if take_time < 1:  # 判断不足1秒时
        take_time = 1  # 设置为1秒
    # 计算花费时间
    m, s = divmod(take_time, 60)
    h, m = divmod(m, 60)

    custom_print("本次花费时间 %02d:%02d:%02d" % (h, m, s),"green")
View Code

执行脚本,效果同上

本文参考链接:

https://www.cnblogs.com/c-x-a/p/9049651.html

<think> 我们面对的问题是:在使用`pool.starmap`时,如何解决`'daemonic process are not allowed to have children'`错误。 这个错误通常发生在尝试在守护进程(daemon process)中创建子进程时。在Python的多进程模块中,当我们使用`multiprocessing.Pool`时,默认情况下,池中的工作进程是守护进程(daemon属性为True)。如果我们在这些工作进程内部再尝试创建新的子进程,就会触发这个错误。 解决方法的核心在于:避免在池工作进程内部创建新的进程。但有时我们的任务函数本身可能需要进行多进程操作(例如嵌套使用进程池),这就会导致问题。 以下是几种可行的解决方案: ### 方法1:使用非守护进程的进程池 我们可以通过自定义一个非守护进程的进程池类来覆盖默认的守护进程设置。因为`multiprocessing.Pool`创建的工作进程默认是守护进程,我们可以通过继承`multiprocessing.pool.Pool`类,并修改工作进程的守护属性为`False`。 但是,请注意,直接创建非守护进程的工作进程可能会导致主进程退出时子进程仍然运行,因此我们需要确保在主进程退出前正确关闭进程池。 示例代码: ```python import multiprocessing import multiprocessing.pool class NoDaemonProcess(multiprocessing.Process): # 创建一个非守护进程的进程类 def _get_daemon(self): return False def _set_daemon(self, value): pass daemon = property(_get_daemon, _set_daemon) # 继承multiprocessing.pool.Pool,使用我们自定义的非守护进程类 class MyPool(multiprocessing.pool.Pool): Process = NoDaemonProcess # 然后使用这个自定义的MyPool def your_function(x, y): # 在这个函数内部,我们可以创建子进程 # 例如,可能再次使用进程池(注意避免无限递归资源耗尽) return x * y if __name__ == '__main__': with MyPool(processes=4) as pool: results = pool.starmap(your_function, [(1,2), (3,4), (5,6)]) print(results) ``` 这种方法通过自定义进程池,使得池中的工作进程不再是守护进程,从而允许它们创建子进程。 ### 方法2:避免在任务函数中嵌套使用多进程 如果可能,重构代码,避免在传递给`starmap`的任务函数中再创建新的进程。这通常是最优的解决方案,因为嵌套多进程会带来复杂性资源消耗。 如果任务需要并发执行,考虑将嵌套的多进程操作改为多线程(如果I/O密集型)或者使用协程。或者,重新设计任务划分,将嵌套的并发操作提到外层。 ### 方法3:使用`multiprocessing.get_context()`创建子进程 在某些情况下,可以通过改变子进程的启动方式来解决。例如,在任务函数内部创建子进程时,使用`spawn`上下文。但注意,这并不总是能解决问题,因为守护进程的限制仍然存在。 但如果我们已经使用了方法1(非守护进程池),那么任务函数内部再创建子进程就没有问题了。 ### 方法4:使用`concurrent.futures.ProcessPoolExecutor` 另外,我们可以考虑使用`concurrent.futures`模块中的`ProcessPoolExecutor`。但是,它创建的工作进程同样是守护进程,所以同样的问题会发生。因此,我们需要自定义执行器以使用非守护进程,这需要更复杂的技巧。 因此,推荐使用方法1,即自定义非守护进程池。 ### 注意事项 1. 使用非守护进程池时,需要确保正确关闭进程池,否则这些进程可能会成为僵尸进程。 2. 嵌套多进程会消耗大量资源(如内存、进程数等),请确保系统资源足够。 3. 在Windows系统中,由于缺少`fork`,多进程编程需要特别注意,确保使用`if __name__ == '__main__'`保护主模块。 下面是一个更完整的示例,展示如何在自定义非守护进程池中使用`starmap`: ```python import multiprocessing import multiprocessing.pool # 自定义非守护进程类 class NoDaemonProcess(multiprocessing.Process): @property def daemon(self): return False @daemon.setter def daemon(self, value): pass # 自定义进程池,使用非守护进程 class MyPool(multiprocessing.pool.Pool): Process = NoDaemonProcess # 假设我们的任务函数内部需要创建子进程 def inner_task(x): return x * x def outer_task(x, y): # 在任务内部再创建子进程 # 这里只是示例,实际中可能更复杂 with multiprocessing.Pool(1) as inner_pool: result = inner_pool.map(inner_task, [x, y]) return sum(result) if __name__ == '__main__': # 创建自定义进程池 pool = MyPool(processes=2) # 使用starmap data = [(1, 2), (3, 4), (5, 6)] results = pool.starmap(outer_task, data) pool.close() pool.join() print(results) ``` 在这个示例中,`outer_task`函数内部创建了一个新的进程池(即使只有一个进程)。由于我们使用了自定义的非守护进程池`MyPool`,所以不会出现`daemonic process`错误。 但是,请注意:嵌套使用进程池可能会导致系统资源紧张,应根据实际情况调整进程数量。 最后,如果问题发生在使用第三方库(如PyTorch)的多进程处理中,可能还需要设置环境变量或启动方法,如: ```python import multiprocessing multiprocessing.set_start_method('spawn', force=True) ``` 因为不同的启动方法(fork, spawn, forkserver)对守护进程的处理可能不同。 §§相关问题§§ 1. 如何在Python中正确关闭嵌套的多进程池? 2. 多进程编程中如何避免死锁? 3. 如何监控多进程程序的资源使用情况? 4. 守护进程非守护进程在资源清理上有何不同? [^1]: Python官方文档 multiprocessing — Process-based parallelism [^2]: multiprocessing.pool.Pool 类文档 </think> # 解决`pool.starmap`中的`daemonic process are not allowed to have children`错误 在使用`pool.starmap`时遇到此错误,是因为Python禁止守护进程创建子进程。以下是系统解决方案: ## 核心解决方法 ### 方法1:使用`spawn`启动方法(推荐) ```python import multiprocessing as mp def task_with_subprocess(x, y): # 此函数内部会创建子进程 with mp.Pool(processes=2) as inner_pool: return inner_pool.starmap(some_function, [(x, i) for i in range(y)]) if __name__ == '__main__': # 设置启动方法为spawn mp.set_start_method('spawn', force=True) data = [(1, 5), (2, 3), (3, 4)] with mp.Pool() as pool: results = pool.starmap(task_with_subprocess, data) print(results) ``` ### 方法2:使用`forkserver`启动方法 ```python if __name__ == '__main__': mp.set_start_method('forkserver', force=True) # 其余代码同上 ``` ### 方法3:自定义非守护进程池 ```python class NonDaemonPool(mp.pool.Pool): def __init__(self, *args, **kwargs): kwargs['context'] = mp.get_context('spawn') super().__init__(*args, **kwargs) def _repopulate_pool(self): # 覆盖内部方法,创建非守护进程 for i in range(self._processes - len(self._pool)): w = self.Process(target=worker, daemon=False) self._pool.append(w) w.start() if __name__ == '__main__': with NonDaemonPool(processes=4) as pool: results = pool.starmap(task_with_subprocess, [(1, 2), (3, 4)]) ``` ## 关键注意事项 1. **启动方法选择**: - `fork`(默认):继承父进程资源,但会导致守护进程问题 - `spawn`:创建全新进程,避免守护进程限制 - `forkserver`:预加载资源池,提高性能 2. **代码结构要求**: ```python if __name__ == '__main__': # 所有进程启动代码必须放在此处 ``` 3. **参数传递优化**: ```python # 避免在starmap内创建复杂对象 results = pool.starmap(process_data, [(x, y) for x, y in simple_data]) ``` ## 替代方案:避免嵌套进程池 ```python def process_item(x, y): # 将嵌套并行改为单线程处理 return [some_function(x, i) for i in range(y)] if __name__ == '__main__': with mp.Pool() as pool: results = pool.starmap(process_item, [(1, 5), (2, 3)]) ``` ## 常见应用场景 1. **PyTorch DataLoader**: ```python import torch mp.set_start_method('spawn', force=True) loader = DataLoader(dataset, num_workers=4) ``` 2. **TensorFlow并行处理**: ```python import tensorflow as tf strategy = tf.distribute.MirroredStrategy() ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值