重新启动Python中的线程 [英] Restarting a thread in Python

查看:358
本文介绍了重新启动Python中的线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为Python 3.4中的项目制作线程飞行软件,其中需要线程来重新启动自身,以防在读取传感器或发生类似此类意外事件时发生I / O错误。因此,我正在做一个看门狗,以检查线程是否已死亡并重新启动它们。

I'm trying to make threaded flight software for a project in Python 3.4, in which I need threads to restart themselves in case an I/O error occurs during a sensor read or another fluke crash like that. Therefore I am working on making a watchdog to check if threads have died and restarting them.

首先,我试图仅检查线程是否不再处于活动状态并重新启动它,这是这样做的:

At first I attempted to just check if the thread was no longer alive and restart it, which did this:

>>> if not a_thread.isAlive():
...     a_thread.start()
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "c:\Python34\lib\threading.py", line 847, in start
    raise RuntimeError("threads can only be started once")
RuntimeError: threads can only be started once

之后才启动。从线程的角度来看,这种行为是合理的和Python本身,但使我的工作更加困难。因此,我使用字典实现了一个解决方案,以存储初始线程并将其复制到新对象并在必要时启动它。不幸的是,这也不起作用。
这是一个基本示例:

This behaviour makes sense from the standpoint of threadingand Python itself, but makes my job harder. So I implemented a solution using a dictionary to store the initial thread and copy it to a new object and start it when necessary. Unfortunately this doesn't work either. Here's a basic example:

import threading
import logging
import queue
import time
from copy import copy, deepcopy

def a():
    print("I'm thread a")
def b():
    print("I'm thread b")

# Create thread objects
thread_dict = {
'a': threading.Thread(target=a, name='a'),
'b': threading.Thread(target=b, name='b')
}

threads = [copy(t) for t in thread_dict.values()]

for t in threads:
    t.start()
for i in range(len(threads)):
    if not threads[i].isAlive():
        temp = thread_dict[threads[i].name]
        threads[i] = deepcopy(temp)
        threads[i].start()
    thread(i).join(5)

返回:

I'm thread a
I'm thread b
Traceback (most recent call last):
  File "main_test.py", line 25, in <module>
    threads[i] = deepcopy(temp)
  File "c:\Python34\lib\copy.py", line 182, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  ... (there's about 20 lines of traceback within copy)
  File "c:\Python34\lib\copyreg.py", line 88, in __newobj__
    return cls.__new__(cls, *args)
TypeError: object.__new__(_thread.lock) is not safe, use _thread.lock.__new__()

所以显然 threading 对象是不安全的复制...无论如何,是否有重新启动线程的必要性整个对象?

So apparently threading objects are not safe to copy... Is there anyway to restart threads short of recreating the entire object?

推荐答案

没有理由让线程死掉。


如果

There's no reason to let your threads die.

If they're actually crashing, your whole program will crash.

如果它们只是引发异常,则可以捕获异常。

If they're just raising exceptions, you can just catch the exceptions.

如果

您甚至可以简单地包装一个线程函数以在异常或返回时自行重启:

You can even trivially wrap a thread function to restart itself on exception or return:

def threadwrap(threadfunc):
    def wrapper():
        while True:
            try:
                threadfunc()
            except BaseException as e:
                print('{!r}; restarting thread'.format(e))
            else:
                print('exited normally, bad thread; restarting')
    return wrapper

thread_dict = {
    'a': threading.Thread(target=wrapper(a), name='a'),
    'b': threading.Thread(target=wrapper(b), name='b')
}    

问题已解决。

大多数平台都无法这样做。

Most platforms have no way to do so.

从概念上讲,这没有任何意义。当线程完成时,其堆栈已死;其父项已被标记或发出信号;一旦加入,其资源将被销毁(包括内核级资源,例如其进程表条目)。重新启动它的唯一方法是创建一组全新的内容。您可以通过创建新线程来做到这一点。

And conceptually, it doesn't make any sense. When a thread finished, its stack is dead; its parent is flagged or signaled; once it's joined, its resources are destroyed (including kernel-level resources like its process table entry). The only way to restart it would be to create a whole new set of everything. Which you can already do by creating a new thread.

因此,只需执行此操作即可。如果您真的不想在内部处理异常,则只需存储构造参数并使用它们来启动新线程即可。

So, just do it. If you really don't want to handle the exceptions internally, just store the construction arguments and use them to start a new thread.

您甚至可以创建自己的子类来挂在它们上,以用于您:

You can even create your own subclass that hangs onto them for you:

class RestartableThread(threading.Thread):
    def __init__(self, *args, **kwargs):
        self._args, self._kwargs = args, kwargs
        super().__init__(*args, **kwargs)
    def clone(self):
        return RestartableThread(*args, **kwargs)

现在可以很容易地复制线程(具有所需的语义):

And now it's easy to "copy" the thread (with the semantics you wanted):

if not a_thread.is_alive():
    a_thread = a_thread.clone()




是的, threading.Thread 对象不安全地复制


您期望发生什么?充其量,您将获得围绕同一OS级线程对象的不同包装器,因此您将使Python没注意到您正在尝试执行非法的,可能引起段错误的操作,从而阻止了您


Yes, threading.Thread objects are not safe to copy

What would you expect to happen? At best, you'd get a different wrapper around the same OS-level thread object, so you'd fool Python into not noticing that you're trying to do the illegal, possibly segfault-inducing things it was trying to stop you from doing.

这篇关于重新启动Python中的线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆