从线程目标访问`self`

时间:2017-12-20 20:32:46

标签: python multithreading python-3.x

根据多个来源,包括this question,将可运行作为__init__中的target参数传递(包含或不包含argskwargs)优于扩展Thread类。

如果我创建了一个runnable,我怎样才能将它运行的线程self传递给它而不扩展Thread类?例如,以下内容可以正常工作:

class MyTask(Thread):
    def run(self):
        print(self.name)
MyTask().start()

但是,我看不到让这个版本起作用的好方法:

def my_task(t):
    print(t.name)
Thread(target=my_task, args=(), kwargs={}).start()

这个问题是Python - How can I implement a 'stoppable' thread?的后续问题,我回答说,但可能不完整。

更新

我已经考虑过使用current_thread()进行黑客攻击:

def my_task():
    print(current_thread().name)
Thread(target=my_task).start()

问题:调用函数来获取理想情况下应该传入的参数。

更新#2

我找到了一个更加狡猾的解决方案,使current_thread看起来更具吸引力:

class ThreadWithSelf(Thread):
    def __init__(self, **kwargs):
        args = kwargs.get('args', ())
        args = (self,) + tuple(args)
        kwargs[args] = args
        super().__init__(**kwargs)
ThreadWithSelf(target=my_task).start()

除了令人难以置信的丑陋(例如,强迫用户仅使用关键字,即使这是文档中推荐的方式),这完全违背了不扩展Thread的目的

更新#3

另一个荒谬(和不安全)的解决方案:通过args传递一个可变对象并在之后更新它:

def my_task(t):
    print(t[0].name)
container = []
t = Thread(target=my_task, args=(container,))
container[0] = t
t.start()

为了避免同步问题,你可以将其提升一个档次并实现另一层荒谬:

 def my_task(t, i):
     print(t[i].name)
 container = []
 container[0] = Thread(target=my_task, args=(container, 0))
 container[1] = Thread(target=my_task, args=(container, 1))
 for t in container:
     t.start()

我仍然在寻找一个合理的答案。

3 个答案:

答案 0 :(得分:3)

您的目标似乎是从任务本身访问当前正在执行任务的线程。您不能将该线程作为参数添加到threading.Thread构造函数中,因为它尚未构造。我认为有两个真正的选择。

  1. 如果您的任务运行多次,可能在许多不同的线程上运行,我认为最好的选择是在任务中使用threading.current_thread()。这使您可以直接访问线程对象,您可以使用它来执行任何操作。这似乎正是这个函数的用例。

  2. 另一方面,如果您的目标是实现具有某些特殊特征的线程,那么自然的选择是子类threading.Thread,实现您希望的任何扩展行为。

  3. 另外,正如您在评论中指出的那样,insinstance(current_thread(), ThreadSubclass)将返回True,这意味着您可以使用这两个选项,并确保您的任务可以访问您已实施的任何额外行为在你的子类中。

答案 1 :(得分:1)

您可以使用可变对象(如list)作为线程的参数,并在创建后填充它,但在运行之前:

def my_task(l):
    t = l[0]
    print(t.name)


mutable_list = []
thread = threading.Thread(target=my_task, args=(mutable_list,), kwargs={})
mutable_list.append(thread)
thread.start()
thread.join()

你得到:

Thread-1

答案 2 :(得分:1)

这里最简单,最易读的答案是:使用current_thread()。您可以使用各种奇怪的方法将线程作为参数传递,但没有充分的理由。调用current_thread()是标准方法,它比所有替代方案都短,并且不会混淆其他开发人员。不要试图过度思考/过度设计这个:

def runnable():
    thread = threading.current_thread()

Thread(target=runnable).start()

如果你想稍微隐藏它并因审美原因而改变,你可以尝试:

def with_current_thread(f):
    return f(threading.current_thread())

@with_current_thread
def runnable(thread):
    print(thread.name)

Thread(target=runnable).start()

如果这还不够好,你可以通过描述为什么你认为参数传递对你的用例更好/更正确来得到更好的答案。