1

之前,此问题是由以下问题跟进:With statement and python threading

我一直在使用Python线程API试验。 我有这样的代码,它适用于我想要实现的功能:---->在调用python线程运行之前执行函数执行。但是要做到这一点,我总是不得不在run()方法中调用time.sleep(1)以使其继续执行(),否则线程将退出而不进行函数分配和执行。是否有更好的方法实现这种等待?随着句和线程:让函数执行运行

from __future__ import print_function 
import threading 
import time 
import functools 
import contextlib 
import thread 
from threading import Lock 
#import contextlib 
#Thread module for dealing with lower level thread operations.Thread is limited use Threading instead. 

def timeit(fn): 
    '''Timeit function like this doesnot work with the thread calls''' 
    def wrapper(*args,**kwargs): 
     start = time.time() 
     fn(*args,**kwargs) 
     end = time.time() 
     threadID = "" 
     print ("Duration for func %s :%d\n"%(fn.__name__ +"_"+ threading.current_thread().name ,end-start)) 
    return wrapper 

exitFlag = 0 

@timeit 
def print_time(counter,delay): 
    while counter: 
     if exitFlag: 
      thread.exit() 
     time.sleep(delay) 
     print("%s : %s_%d"%(threading.current_thread().name,time.ctime(time.time()),counter)) 
     counter -= 1 

class Mythread(threading.Thread): 
    def __init__(self,threadID,name): 
     threading.Thread.__init__(self) 
     self.threadID = threadID 
     self.name = name 
     self._f = None 

    def run(self): 
     print("Starting%s\n" % self.name) 
     time.sleep(1) 
     if self._f: 
      self._f() 
      print("Exiting%s\n" % self.name) 
     else: 
      print("Exiting%s without function execution\n" % self.name) 

#  def set_f(self,f): 
#   self._f = f 

    def execute(self,f,*args,**kwargs): 
     self._f=functools.partial(f,*args,**kwargs) 

    def __enter__(self): 
     self.start() 

    def __exit__(self,type,value,traceback): 
     self.join() 




class ThreadContainer(object): 
    def __init__(self,id,name): 
     self._t = Mythread(id,name) 

    def execute(self,f,*args,**kwargs): 
     self._f=functools.partial(f,*args,**kwargs) 
     self._t.set_f(self._f) 
#  self._t.start() 
#   self._t.join() 


    def __enter__(self): 
     self._t.start() 

    def __exit__(self,type,value,traceback): 
     self._t.join() 




if __name__ == '__main__': 
    ''' 
    print_time(5, 1) 
    threadLock = threading.Lock() 
    threads = [] 
    thread1 = Mythread(1,"Thread1",5,1) 
    thread2 = Mythread(2,"Thread2",5,2) 
    thread1.start() 
    thread2.start() 
    threads.append(thread1) 
    threads.append(thread2) 
    for t in threads: 
     t.join() 
    ''' 
#  thread1 = Mythread(1,"Thread1") 
#  thread2 = Mythread(2,"Thread2") 
#  with contextlib.nested(ThreadContainer(1,"Thread1"),ThreadContainer(2,"Thread2")) as (t1,t2): 
#   t1.execute(print_time,5,1) 
#   t2.execute(print_time,5,2) 
    t1 = Mythread(1,"Thread1") 
    t2 = Mythread(2,"Thread2") 
    with contextlib.nested(t1,t2): 
     t1.execute(print_time,5,1) 
     t2.execute(print_time,5,2) 


    print("Exiting main thread ") 
+0

如果你剥离它们,你的问题会更容易回答 - 删除注释掉的代码,你永远不会实例化的类,等等,如果它们与这个问题无关,并且提供一个最小的例子来展示你所问​​的东西(理想的情况是一个不需要滚动读取)。 – abarnert 2013-05-07 18:02:19

回答

1

这里的问题是,你希望run功能要等到execute函数被调用。

当然明显的解决方法是调用execute打电话之前start

t1.execute(print_time,5,1) 
t2.execute(print_time,5,2) 
with contextlib.nested(t1, t2): 
    pass 

...或只是让execute电话start,或传递函数的构造函数或start呼叫,或...

此外,你的设计有点奇怪。线程函数被设计用于处理_f尚未设置的情况......但您希望它等到_f已被设置?


但是这是可以想象的,这种问题可以想出一个更现实的设计,所以,让我们来看看如何解决这个问题。

首先,添加sleep来解决线程问题几乎总是表明您正在做的事情非常错误。这也是导致可怕性能问题的好方法(例如:在足够多的地方添加足够的sleep s以使所有内容都工作正常,需要30秒才能启动应用程序,而不是30毫秒) - 更糟糕的是, ,比赛条件错误(确保1秒总是足够的时间,对吗?除非计算机正在进行交换,或从休眠中醒来,或者忙于其他使用所有CPU的程序,或...)。

如果您尝试跨线程同步操作,则需要使用同步对象。诀窍是知道正确的。通过Event阅读Lock的文档(和3.x增加了Barrier),并找到在一般线程得到一个什么样所有这些事情是更广阔的想法的教程。*

在这种情况下,你已经得到的代码正在等待一些更改以保存状态,而其他代码正在进行更改,这是'Condition'的典型用例。所以:

class Mythread(threading.Thread): 
    def __init__(self, threadID, name, condition): 
     self.condition = condition 
     # ... same as before 

    def run(self): 
     # ... setup before checking for _f 

     with self.condition: 
      while not self._f: 
       self.condition.wait() 
     self._f() 

     # ... anything else you want 

现在,你需要创建Condition,它传递给线程,notify它。

你可以使用一个单一的Condition

condition = threading.Condition() 
t1 = Mythread(1, "Thread1", condition) 
t2 = Mythread(2, "Thread2", condition) 
with contextlib.nested(t1,t2): 
    with condition: 
     t1.execute(print_time, 5, 1) 
     t2.execute(print_time, 5, 2) 
     condition.notify_all() 

或者,你可以为每个线程提供自己的Condition

class Mythread(threading.Thread): 
    def __init__(self, threadID, name): 
     self.condition = Condition() 
     # ... same as before 

# ... 

t1 = Mythread(1, "Thread1") 
t2 = Mythread(2, "Thread2") 
with contextlib.nested(t1,t2): 
    with t1.condition: 
     t1.execute(print_time, 5, 1) 
     t1.condition.notify() 
    with t2.condition: 
     t2.execute(print_time, 5, 1) 
     t2.condition.notify() 

请注意,这并不让你以明确的“未设置”_f,但这很容易做到这一点。例如,您可以添加_f_set属性,并检查该属性,而不是_f,因此有人可以拨打execute(None)(然后notify)将您唤醒并转到“否_f”的情况。


*警告:某些命名不一致。还有一种叫做“屏障”的不同的东西,另一种不同的东西也被称为“屏障”,并且有许多与“蟒蛇”不同的“事件”变体(其中一些更像是一种条件,但不是实际上可以这样使用),有时一个“条件变量”是由同步对象而不是同步对象保护的实际共享状态,依此类推...