根据多个来源,包括this question,将可运行作为__init__
中的target
参数传递(包含或不包含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()
我仍然在寻找一个合理的答案。
答案 0 :(得分:3)
您的目标似乎是从任务本身访问当前正在执行任务的线程。您不能将该线程作为参数添加到threading.Thread
构造函数中,因为它尚未构造。我认为有两个真正的选择。
如果您的任务运行多次,可能在许多不同的线程上运行,我认为最好的选择是在任务中使用threading.current_thread()
。这使您可以直接访问线程对象,您可以使用它来执行任何操作。这似乎正是这个函数的用例。
另一方面,如果您的目标是实现具有某些特殊特征的线程,那么自然的选择是子类threading.Thread
,实现您希望的任何扩展行为。
另外,正如您在评论中指出的那样,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()
如果这还不够好,你可以通过描述为什么你认为参数传递对你的用例更好/更正确来得到更好的答案。