【问题标题】:Spawning a thread in python在python中产生一个线程
【发布时间】:2010-05-21 13:11:11
【问题描述】:

我有一系列“任务”,我想在单独的线程中运行。这些任务将由单独的模块执行。每个都包含用于处理其任务的业务逻辑。

给定一组任务,我希望能够为每个模块生成一个新线程,如下所示。

from foobar import alice, bob charles
data = getWorkData()
# these are enums (which I just found Python doesn't support natively) :(
tasks = (alice, bob, charles)

for task in tasks
  # Ok, just found out Python doesn't have a switch - @#$%!
  # yet another thing I'll need help with then ...
  switch
    case alice:
      #spawn thread here - how ?
      alice.spawnWorker(data)

没有奖赏我还在用 C++ 思考。如何使用 Pythonic 'enums' 和 'switch'es 以 Pythonic 方式编写它,并能够在新线程中运行模块。

显然,这些模块都有一个派生自 ABC(抽象基类)的类,称为 Plugin。 spawnWorker() 方法将在 Plugin 接口上声明并在各个模块中实现的类中定义。

也许,有更好的(即 Pythonic)方式来完成这一切?我很想知道

[编辑]

我刚刚阅读了更多机器人,但似乎 Python 并没有实现真正意义上的线程(至少,不是 C++ 程序员所认为的意义上的)。在任何情况下,这对我来说都不是表演的终结者。每个任务都相当耗时,我不想在另一个任务完成之前等待启动一个任务,这就是我使用线程的原因。时间切片并没有给我带来太多困扰——只要它们几乎同时启动(或彼此紧随其后),Python 就可以根据需要在踏板之间进行时间切片——这对我来说很好。

我在 SO 上看到了类似问题的答案。

用户提供了一个简单的线程类,如下所示:

import threading
class Foo (threading.Thread):
    def __init__(self,x):
        self.__x = x
        threading.Thread.__init__(self)
    def run (self):
          print str(self.__x)

for x in xrange(20):
    Foo(x).start()

我正在考虑将它用于我的 ABC 插件。然后我的问题是我将代码放在哪里完成实际任务(即业务逻辑)。我假设这在 Foo 类的 run() 方法中(我知道一个明显的问题,但我不想做任何假设)。

我的想法是正确的还是有缺陷的(如果有缺陷 - 我错过了什么?)

【问题讨论】:

  • 为什么不使用适当的多态性(ABC 继承或鸭子类型)而不是 switch-case?
  • @Santa:好点子。这就是我在 C++ 中的做法(多态性)。但我不太确定 Python 是否支持。
  • @morpheous 您会发现,在传统的基于继承的多态性之上,Python 还支持更多动态的多态性方法,其中最突出的是鸭子类型。

标签: python


【解决方案1】:

为什么不使用适当的多态而不是 switch-case?例如,在这里你可以用 Python 中的鸭子类型做什么:

比如alice.py:

def do_stuff(data):
    print 'alice does stuff with %s' % data

比如bob.py:

def do_stuff(data):
    print 'bob does stuff with %s' % data

然后在您的客户端代码中,例如,main.py:

import threading
import alice, bob

def get_work_data():
    return 'data'

def main():
    tasks = [alice.do_stuff, bob.do_stuff]
    data = get_work_data()
    for task in tasks:
        t = threading.Thread(target=task, args=(data,))
        t.start()

如果我需要澄清,请告诉我。

【讨论】:

  • +1 您的代码既漂亮又简单-但是,您没有将数据传递给生成的线程-您能否修改您的代码以显示数据如何传递给生成的线程(例如在我的伪代码)?交易
  • 请注意,如果data 碰巧是可变的,您需要将副本传递给每个线程,或者也传递一个锁定对象 (docs.python.org/library/threading.html#lock-objects)。
【解决方案2】:
import threading
from foobar import alice, bob, charles

data = get_work_data() # names_in_pep8 are more Pythonic than camelCased

for mod in [alice, bob, charles]:
    # mod is an object that represent a module
    worker = getattr(mod, 'do_work')
    # worker now is a reference to the function like alice.do_work
    t = threading.Thread(target=worker, args=[data])
    # uncomment following line if you don't want to block the program
    # until thread finishes on termination
    #t.daemon = True 
    t.start()

把你的逻辑放到对应模块的do_work函数中。

【讨论】:

  • 好答案,但你的最后一行应该是t.start()。
  • +1 我真的很喜欢这个答案,因为我似乎可以直接迭代模块(你能确认是这种情况吗? - 这太酷了)。如果答案是肯定的,这意味着只要每个模块都有一个名为'do_work'的函数,那么上面的代码就会产生线程并在每个模块中在单独的线程中运行do_work()函数(我的理解是否正确?)。看起来最后一个方法调用应该是 start() 对吧?
  • @morpheus:你是对的。在 Python 中,模块也是一流的对象。您可以将其传递给函数,将其放入列表等。是的,应该向线程发送start 方法。
  • @santa,@tgray。是的,应该有start 而不是run
【解决方案3】:

顺序执行:

from foobar import alice, bob, charles

for fct in (alice, bob, charles):
    fct()

并行执行:

from threading import Thread
from foobar import alice, bob, charles

for fct in (alice, bob, charles):
    Thread(target=fct).start()

【讨论】:

  • @nailxx run 方法是您定义该线程执行所需工作的地方。 start 方法是您需要发送到线程对象以在单独的执行线程中执行其run 的方法。否则,您只是在当前线程中运行它,因此违背了将 Thread 定义为开头的目的。
  • @nailxx,我在您的帖子的评论中添加了一个文档链接,该链接解释了这一点。
【解决方案4】:

Python 可以将函数保存为对象。为了克服缺少开关的限制,我可以提出以下建议:

case_alice = lambda data : alice.spawnWorker(data)

my_dict[alice] = case_alice

组成一个字典来保存你的“案例”陈述。

让我更进一步:

data = getWorkData()
case_alice = lambda d : alice.spawnWorker( d )
case_bob = lambda d : bob.spawnWorker( d )
case_charles = lambda d : charles.spawnWorker( d )

switch = { alice : case_alice, bob : case_bob, charles : case_charles }
spawn = lambda person : switch[ person ]( data )
[ spawn( item ) for item in (alice, bob, charles )]

【讨论】:

  • @wheaties:Pythonic 代码 +1(仍在阅读以确保我理解那里发生的所有事情)。您能否通过如何使用多处理模块实际产生线程(或进程 - 没关系)来扩展您的 sinppet
  • lambda 完全没有必要。只需执行my_dict[alice] = alice.spawnWorker 和[ switch[item](data) for item in ... ]
  • @morpheous 没有使用多处理库的经验。 @Thomas Wouters 你是绝对正确的。但是,我认为让它们保持原样更有利于将函数理解为第一类对象。当我第一次学习时,看到 lambda 让我想起了。
  • 不过,lambda 在很大程度上是不必要的。我自己从来没有在我的项目中使用过它们,而且 Guido 似乎也不喜欢它。
  • 我不知道如何使用 lambda 来假装 spawnWorker 方法不是一等对象有助于理解函数是一等对象:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-04
  • 2015-01-11
  • 2014-11-13
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多