1

この質問は、次の質問のフォローアップです: With statement and python threading

私はpython threading apiを試しています。私は達成したいことのために機能するこのコードを持っています:----> Pythonスレッドで実行を呼び出す前に関数を実行します。

ただし、これを行うには、常に run() メソッドで time.sleep(1) を呼び出して、execute() に進む必要があります。そうしないと、スレッドは関数の割り当てと実行なしで終了します。このタイプを実現するためのより良い方法はありますか?待っている?

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 ")
4

1 に答える 1

1

ここでの問題は、関数が呼び出されるrunまで関数を待機させたいことです。execute

もちろん、明白な解決策は、呼び出すexecute前に呼び出すことですstart:

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

…または単にexecutecallを作成するstartか、関数をコンストラクターまたはstart呼び出しに渡すか、または…</p>

また、意図したデザインは少し奇妙です。スレッド関数は、_f設定されていないケースを処理するように設計されていますが、設定されるまで待機させたい_fですか?


しかし、この種の問題は、より現実的な設計で発生する可能性があると考えられるので、それを解決する方法を見てみましょう.

まず、sleepスレッドの問題を解決するために追加することは、ほとんどの場合、何か非常に間違ったことをしている兆候です。また、これはいずれかの恐ろしいパフォーマンスの問題を引き起こす優れた方法でもあります (たとえば、sleepすべてがほとんど機能するのに十分な数の s を十分な場所に追加するまでに、アプリの起動に 30 ミリ秒ではなく 30 秒かかります)。さらに悪いことに、競合状態のバグ (常に 1 秒で十分ですよね? コンピューターがスワップをスラッシングしている場合、休止状態から復帰している場合、または他のプログラムですべての CPU を使用している場合を除きます)。

スレッド間でアクションを同期しようとしている場合は、同期オブジェクトを使用する必要があります。コツは、正しいものを知ることです。LockスルーEvent(および 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()

これにより、明示的な "not set" は許可されないことに注意してください_f。ただし、それを行うのは非常に簡単です。たとえば、_f_set属性を追加して、 の代わりにそれをチェックすると、誰かが(次に) を_f呼び出して、あなたを目覚めさせ、「いいえ」のケースに導くことができます。execute(None)notify_f


※注意:名前が一部間違っています。「バリア」とも呼ばれる別のものと、「フェンス」とも呼ばれる別のものがあります。また、Python とはかなり異なる「イベント」の変種が多数あります (そのうちのいくつかは条件に似ていますが、そうではありません)。実際にはそのまま使用可能)、「条件変数」は、同期オブジェクトではなく同期オブジェクトによって保護された実際の共有状態である場合もあります…</p>

于 2013-05-07T18:22:33.530 に答える