在Python中终止线程并释放内存

3

我像这里所展示的那样杀死线程:在Python中有没有办法杀死一个线程?

但是我注意到,内存没有被释放(gc.get_objects()不断增长)。实际上,这些对象是列表、字典等,而不是文件。

有没有办法手动释放资源? 代码:

import ctypes

def terminate_thread(thread):
    """Terminates a python thread from another thread.

    :param thread: a threading.Thread instance
    """
    if not thread.isAlive():
        return

    exc = ctypes.py_object(SystemExit)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
        ctypes.c_long(thread.ident), exc)
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.result = None
        self.error = None

    def run(self):
        try:
            self.result = myfun(*args, **kw) #run external resource and the interrupt it
        except Exception as e:
            self.error = e

c = MyThread()
c.start()
c.join(60) # wait a minute
counter = 0
if c.isAlive():
    while c.isAlive():
        time.sleep(0.1)
        try:
            terminate_thread(c) # how to release resources?
        except:
            break
        counter += 1
        if counter > 10: break
    raise TimeoutException

输出示例:

print('Controlled objects: %s' % len(gc.get_objects()))
print ('Unreachable: %s' % gc.collect())

Controlled objects: 85084 #request 1, no timeout
Unreachable: 5640

Controlled objects: 171994 # request 2, timeout
Unreachable: 7221

2
请创建一个尽可能少的代码示例,展示出问题。目前而言,你的问题的答案就是“不可能”…没有办法让父线程结束子线程。 - Joran Beasley
你已经接近了……但仍然没有被调用的 myfun……按照现在的情况,由于 myfun 不存在的错误,这应该会顺利退出线程,没有问题。 - Joran Beasley
myfun是外部库,无法在此处复制。假设它能够正常工作,但有时会运行时间较长,因此需要中断。 - Paul R
1
如果myfun是外部函数,则在您复制的示例中注意注释:正如文档中所述,这并不是万能解决方案,因为如果线程在Python解释器之外忙碌,它将无法捕获中断。 - Mark Tolonen
@MarkTolonen .... 哎呀,你比我快了... - Joran Beasley
显示剩余9条评论
1个回答

4

好的,在所有这些无关紧要的内容之后,我认为你想要的是multiprocessing模块,因为我相信你实际上可以在其中发送sigkill信号。

class MyThread:
    def __init__(self):
        self.result = None
        self.error = None
    def start(self):
        self.proc = multiprocessing.Process(target=self.run)
        self.proc.start()
    def stop(self):
       self.proc.send_signal(multiprocessing.SIG_KILL)
    def run(self):
        try:
            self.result = myfun(*args, **kw) #run external resource and the interrupt it
        except Exception as e:
            self.error = e

如果需要中断带有sig_kill的线程,则应调用c.stop()(当然,其他内容也应适当响应此操作)

您甚至可以直接使用内置的subprocess.Process.kill()(请参见文档https://docs.python.org/2/library/subprocess.html#subprocess.Popen.send_signal

关于您的问题

(是否有手动释放资源的方法?)

 t = Thread(target=some_long_running_external_process)
 t.start()

无法从some_long_running_external_process外部退出线程t


你比我快了 :) 在multiprocessing中也有一个.terminate()方法。 - Mark Tolonen
(yeah I added an addendum :P) - Joran Beasley
我无法使用多进程,因为我正在使用mod_wsgi。 - Paul R
有没有办法使用 os.kill? - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接