我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python 中 Ctrl+C 不能终

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python 中 Ctrl+C 不能终

本文理论上对multiprocessing.dummy的Pool同样有效。

python2.x中multiprocessing提供的基于函数进程池,join后陷入内核态,按下ctrl+c不能停止所有的进程并退出。即必须ctrl+z后找到残留的子进程,把它们干掉。先看一段ctrl+c无效的代码:

#!/usr/bin/env python
import multiprocessing
import os
import time


def do_work(x):
    print 'Work Started: %s' % os.getpid()
    time.sleep(10)
    return x * x


def main():
    pool = multiprocessing.Pool(4)
    try:
        result = pool.map_async(do_work, range(8))
        pool.close()
        pool.join()
        print result
    except KeyboardInterrupt:
        print 'parent received control-c'
        pool.terminate()
        pool.join()
 

if __name__ == "__main__":
    main()

这段代码运行后,按^c一个进程也杀不掉,最后会残留包括主进程在内共5个进程(1+4),kill掉主进程能让其全部退出。很明显,使用进程池时KeyboardInterrupt不能被进程捕捉。解决方法有两种。

方案一

下面这段是python源码里multiprocessing下的pool.py中的一段,ApplyResult就是Pool用来保存函数运行结果的类

class ApplyResult(object):

    def __init__(self, cache, callback):
        self._cond = threading.Condition(threading.Lock())
        self._job = job_counter.next()
        self._cache = cache
        self._ready = False
        self._callback = callback
        cache[self._job] = self

而下面这段代码也是^c无效的代码

if __name__ == '__main__':
    import threading

    cond = threading.Condition(threading.Lock())
    cond.acquire()
    cond.wait()
    print "done"

很明显,threading.Condition(threading.Lock())对象无法接收KeyboardInterrupt,但稍微修改一下,给cond.wait()一个timeout参数即可,这个timeout可以在map_async后用get传递,把

result = pool.map_async(do_work, range(4))

改为

result = pool.map_async(do_work, range(4)).get(1)

就能成功接收^c了,get里面填1填99999还是0xffff都行

方案二

另一种方法当然就是自己写进程池了,需要使用队列,贴一段代码感受下

#!/usr/bin/env python
import multiprocessing, os, signal, time, Queue

def do_work():
    print 'Work Started: %d' % os.getpid()
    time.sleep(2)
    return 'Success'

def manual_function(job_queue, result_queue):
    signal.signal(signal.SIGINT, signal.SIG_IGN)
    while not job_queue.empty():
        try:
            job = job_queue.get(block=False)
            result_queue.put(do_work())
        except Queue.Empty:
            pass
        #except KeyboardInterrupt: pass

def main():
    job_queue = multiprocessing.Queue()
    result_queue = multiprocessing.Queue()

    for i in range(6):
        job_queue.put(None)

    workers = []
    for i in range(3):
        tmp = multiprocessing.Process(target=manual_function,
                                      args=(job_queue, result_queue))
        tmp.start()
        workers.append(tmp)

    try:
        for worker in workers:
            worker.join()
    except KeyboardInterrupt:
        print 'parent received ctrl-c'
        for worker in workers:
            worker.terminate()
            worker.join()

    while not result_queue.empty():
        print result_queue.get(block=False)

if __name__ == "__main__":
    main()

方案三

使用一个全局变量eflag作标识,让SIG_INT信号绑定一个处理函数,在其中对eflag的值更改,线程的函数中以eflag的值判定作为while的条件,把语句写在循环里,老实说这个方案虽然可以用,但是简直太差劲。线程肯定是可行的,进程应该还需要单独共享变量,非常不推荐的方式

常见的错误方案

这个必须要提一下,我发现segmentfault上都有人被误导了

理论上,在Pool初始化时传递一个initializer函数,让子进程忽略SIGINT信号,也就是^c,然后Pool进行terminate处理。代码

#!/usr/bin/env python
import multiprocessing
import os
import signal
import time


def init_worker():
    signal.signal(signal.SIGINT, signal.SIG_IGN)


def run_worker(x):
    print "child: %s" % os.getpid()
    time.sleep(20)
    return x * x


def main():
    pool = multiprocessing.Pool(4, init_worker)
    try:
        results = []
        print "Starting jobs"
        for x in range(8):
            results.append(pool.apply_async(run_worker, args=(x,)))

        time.sleep(5)
        pool.close()
        pool.join()
        print [x.get() for x in results]
    except KeyboardInterrupt:
        print "Caught KeyboardInterrupt, terminating workers"
        pool.terminate()
        pool.join()


if __name__ == "__main__":
    main()

然而这段代码只有在运行在time.sleep(5)处的时候才能用ctrl+c中断,即前5s你按^c有效,一旦pool.join()后则完全无效!

建议

先确认是否真的需要用到多进程,如果是IO多的程序建议用多线程或协程,计算特别多则用多进程。如果非要用多进程,可以利用Python3的concurrent.futures包(python2.x也能装),编写更加简单易用的多线程/多进程代码,其使用和Java的concurrent框架有些相似.
经过亲自验证,ProcessPoolExecutor是没有^c的问题的,要用多进程建议使用它

参考

  1. http://bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/#georges

  2. http://stackoverflow.com/questions/1408356/keyboard-interrupts-with-pythons-multiprocessing-pool#comment12678760_6191991

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python 中 Ctrl+C 不能终

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Python 中 Ctrl+C 不能终

本文理论上对multiprocessing.dummy的Pool同样有效。python2.x中multiprocessing提供的基于函数进程池,join后陷入内核态,按下ctrl+c不能停止所有的进程并退出。即必须ctrl+z后找到残留的
2023-01-31

python捕获ctrl+c手工中断程序

日常编写调试运行程序过程中,难免需要手动停止,以下两种方法可以捕获ctrl+c立即停止程序1、使用python的异常KeyboardInterrupt try: while 1: pass e
2023-01-31

c++中什么函数不能重载

在 c++ 中,以下类型的函数不能重载:1. 构造函数和析构函数;2. 友元函数;3. 运算符函数;4. 强制类型转换函数。原因是这些函数具有特殊的语义和语法规则,重载可能会导致歧义和错误。C++ 中不能重载的函数在 C++ 中,以下类型
c++中什么函数不能重载
2024-04-22

Mysql在debian系统中不能插入中文的终极解决方案

在debian环境下,彻底解决mysql无法插入和显示中文的问题,需要的朋友可以参考下
2022-11-15

windows中c盘的users文件夹能不能删除

本篇内容介绍了“windows中c盘的users文件夹能不能删除”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!c盘的users是用户文件夹,
2023-07-04

Python不能识别中文问题

若python文件中出现中文字符,运行时会出现如下错误SyntaxError: Non-ASCII character '\xd5' in file sort.py on line 2, but no encoding declared;
2023-01-31

c++中void函数不能赋值为什么

c++kquote>在c++中,void函数不能赋值,原因如下:类型不匹配:void函数没有返回值,类型为void,与赋值运算符操作数类型不兼容。函数指针的本质:void函数没有返回地址,使其无法存储在函数指针中,导致赋值无效。避免混淆:禁
c++中void函数不能赋值为什么
2024-05-09

python中utils包不能导入怎么解决

如果Python中的utils包无法导入,可能有以下几种解决方法:确保utils包所在的路径已经添加到Python的模块搜索路径中。可以通过将utils包所在的路径添加到sys.path中来实现:import syssys.path.ap
python中utils包不能导入怎么解决
2024-04-03

深入理解C语言中*和&的不同功能

指针()指向变量内存地址,而地址运算符(&)获取变量地址。指针使用运算符解除引用以访问值,而地址运算符返回指向变量位置的指针。这些运算符用于动态内存分配、链表和数组操作。深入理解C语言中*和&的不同功能指针(*)和地址运算符(&)是C语言
深入理解C语言中*和&的不同功能
2024-04-03

sublime text3中不能运行python中input()的解决方法

小编给大家分享一下sublime text3中不能运行python中input()的解决方法,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!方法参考自网络,此处只为记录。1、Ctrl + shift + P ,在弹出的输入框
2023-06-14

在 C++ 中,函数指针可以做什么,不能做什么?

函数指针在 c++++ 中用于传递、返回或存储函数,增强了程序灵活性。其功能包括:传递函数作为参数从函数返回函数存储函数在数据结构中事件处理但也有限制:类型安全:指向不同函数的指针可以相互转换,存在运行时错误风险。生命周期管理:必须确保函数
在 C++ 中,函数指针可以做什么,不能做什么?
2024-04-18

python中不同的CSV功能和使用示例分析

这篇文章给大家分享的是有关python中不同的CSV功能和使用示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。python主要应用领域有哪些1、云计算,典型应用OpenStack。2、WEB前端开发,众多大
2023-06-14

python中merge显示变量不能唯一识别怎么解决

在Python中,merge操作可以使用pandas库中的merge函数来实现。如果在merge操作中出现变量不能唯一识别的问题,可以尝试以下几种解决方法:1. 检查变量的数据类型:确保要合并的变量具有相同的数据类型,例如整数、字符串等。如
2023-10-11

学会更新pip是Python开发中不可或缺的技能

Python开发必备技能:如何更新pip?在Python开发中,pip是一个重要的工具,用于管理和安装Python包。随着Python生态系统的不断发展和更新,保持pip的最新版本是非常重要的。本文将介绍如何更新pip,并提供具体的代码示
学会更新pip是Python开发中不可或缺的技能
2024-01-18

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录