Python多线程同步
我正在使用多线程,并尝试使用Python多线程同步,python,multithreading,Python,Multithreading,我正在使用多线程,并尝试使用线程。条件在线程之间同步。问题是这样的: do_something() with condition: set_flag() condition.notify() 我有一条主线。在某些情况下,它会这样做: if not is_flag_set(): with condition: condition.wait(120) 它这样做的原因是,它试图给后台线程一些机会,让它在向前移动之前执行do_something(): do_s
线程。条件
在线程之间同步。问题是这样的:
do_something()
with condition:
set_flag()
condition.notify()
我有一条主线。在某些情况下,它会这样做:
if not is_flag_set():
with condition:
condition.wait(120)
它这样做的原因是,它试图给后台线程一些机会,让它在向前移动之前执行do_something()
:
do_something()
set_flag()
with condition:
condition.notifyAll()
标志是主线程和后台线程都考虑的共享资源。当线程尝试访问标志时,我会添加锁。然而,我认为这里的方法并不能解决问题。主线程可以先检查标志,然后后台线程可以设置标志并执行notifyAll
。这将导致主线程实际浪费120秒
我想要一种方法,它可以确保只有在后台线程的dou_something()
未完成时,主线程才会等待后台线程
编辑:刚刚意识到我忘记了带
语句的。我认为你错过了条件的要点
:你必须检查条件对象内部的条件谓词/变量,而不是外部的条件谓词/变量
condition.wait()的while点是它将等待,直到收到通知为止。它将以低廉的价格等待,不使用CPU电源,也不消耗电池;在接到通知之前,它什么也不做。因此,如果您正确地使用它们,它们会完全按照您的要求执行:“仅当来自后台线程的do\u something()
未完成时,才等待后台线程。”
但你必须正确使用它们。在主线程上,执行以下操作:
with condition:
condition.wait_for(is_flag_set)
do_stuff()
然后,在后台线程上,您会这样通知它:
do_something()
with condition:
set_flag()
condition.notify()
为了简单起见,我删除了超时。如果要确保它等待后台线程执行do_something()
,或2分钟,以较早者为准:
with condition:
if condition.wait_for(is_flag_set, 120):
do_stuff_after_flag_set()
else:
do_stuff_after_timeout()
现在,您可以保证只有在后台线程上完成了dou something
之后,才会调用dou stuff\u after\u flag\u set
,或者由于后台线程花费的时间太长,才会调用dou stuff\u after\u timeout
如果要了解(已编辑)现有代码的问题,请执行以下操作:
if not is_flag_set():
with condition:
condition.wait(120)
您担心主线程会在设置条件之前检查标志。当然可以,;在对条件执行任何操作之前,请先输入if
。这就是为什么您必须使用wait_for
,或者在循环wait
时使用,如中所示;这是确保在通知您已准备好检查标志时检查标志的唯一方法
另外,请注意,这里并没有真正同步任何内容。如果对的调用是\u flag\u set
并且set\u flag
未在中发生,且条件为:
,则该标志在线程之间不同步。(在大多数平台上使用CPython,您几乎总能逃脱惩罚,但如果您寻找的是几乎总能逃脱惩罚的东西,而不是正确的东西,那么首先您真的不需要条件
)
一些注意事项:
- 如果没有Python3.2或更高版本,则只有
等待
,而不是等待
,并且无法判断等待是否成功或超时。除了超时问题之外,c.wait\u for(设置了标志)
基本上与相同,而不是设置标志():c.wait()
。因此,您可以自己构建wait_
(注意到的链接),或者您可以在PyPI上找到一个后端口
- 如果你想知道为什么你需要这样做,维基百科的文章很好地解释了这个额外的复杂性所解决的问题。(如果您已经考虑了所有比赛条件,并且知道不需要
条件
,请使用事件
。)
- 请注意,我使用了
notify
而不是notify\u all
(或notifyAll
,这是notify\u all
的另一个名称,但自Python 2.6以来一直被弃用)。如果只有一个服务员,你只需要通知一个服务员;隐藏起来更简单,也更清楚你的意图。(如果有人看到notify_all
,他们可能会认为您使用的是线程池,而您不是。)
- 还请注意,我将
do_stuff()
放在锁内。这两种方式实际上都无关紧要,除非您要重置标志并在以后再次设置。但如果它真的这样做了,这将保护您免受“错过周期”错误的影响
- 最后,你并不真的需要一个
wait_for
或者一个循环wait
;如果该标志在整个程序中只分配一次,并且该条件只通知一次,则在等待
之后而不是在您尝试之前的if
将具有相同的效果。(同样,使用事件
或其他更简单的同步对象也是如此。)但最好是以安全的方式进行;然后,当你以后编辑一些看起来不相关的东西时,你不会意外地添加种族或死锁
我认为您忽略了条件的要点:您必须检查条件对象内部的条件谓词/变量,而不是外部的条件谓词/变量
condition.wait()的while点是它将等待,直到收到通知为止。它将以低廉的价格等待,不使用CPU电源,也不消耗电池;在接到通知之前,它什么也不做。因此,如果您正确地使用它们,它们会完全按照您的要求执行:“仅当来自后台线程的do\u something()
未完成时,才等待后台线程。”
但你必须正确使用它们。在主线程上,执行以下操作:
with condition:
condition.wait_for(is_flag_set)
do_stuff()
然后,在后台线程上,您会这样通知它:
do_something()
with condition:
set_flag()
condition.notify()
为了简单起见,我删除了超时。如果要确保它等待后台线程执行