从线程目标访问`self`

问题描述 投票:3回答:3

根据许多来源,包括this question,在target(有或没有__init__args)中传递一个可运行的kwargs参数比扩展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()

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

python multithreading python-3.x
3个回答
3
投票

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

  1. 如果您的任务运行多次,可能在许多不同的线程上运行,我认为最好的选择是在任务中使用threading.current_thread()。这使您可以直接访问线程对象,您可以使用它来执行任何操作。这似乎正是这个功能设计的用例。
  2. 另一方面,如果你的目标是实现一个具有一些特殊特征的线程,那么自然的选择是继承threading.Thread,实现你想要的任何扩展行为。

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


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

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()

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

© www.soinside.com 2019 - 2024. All rights reserved.