5

スレッド ロックを使用して簡単なテスト プログラムを作成しました。このプログラムは期待どおりに動作せず、Python インタープリターは文句を言いません。

test1.py:

from __future__ import with_statement
from threading import Thread, RLock
import time
import test2

lock = RLock()

class Test1(object):
    def __init__(self):
        print("Start Test1")
        self.test2 = test2.Test2()
        self.__Thread = Thread(target=self.myThread, name="thread")
        self.__Thread.daemon = True
        self.__Thread.start()
        self.test1Method()

    def test1Method(self):
        print("start test1Method")
        with lock:
            print("entered test1Method")
            time.sleep(5)
            print("end test1Method")

    def myThread(self):
        self.test2.test2Method()

if __name__ == "__main__":
    client = Test1()
    raw_input()

test2.py:

from __future__ import with_statement
import time
import test1

lock = test1.lock

class Test2(object):
    def __init__(self):
        print("Start Test2")

    def test2Method(self):
        print("start test2Method")
        with lock:
            print("entered test2Method")
            time.sleep(5)
            print("end test2Method")

両方のスリープが同時に実行されます。ロックを使用するときに期待したものではありません。

test2Method を test1.py に移動すると、すべて正常に動作します。test2.py でロックを作成して test1.py にインポートすると、すべて正常に動作します。別のソース ファイルでロックを作成し、test1.py と test2.py の両方にインポートすると、すべて正常に動作します。

おそらく、サーキュラーの輸入に関係しているのでしょう。

しかし、なぜpythonはこれについて文句を言わないのですか?

4

3 に答える 3

3

Python で python スクリプトを実行する $ python test1.pyと、の代わりにtest1.pyとしてインポートされるため、起動したスクリプトで定義されたロックを取得したい場合は、そうすべきではありませんが、最初のスクリプトを実行すると、( )とは異なる別のロックを作成します。__main__test1import test1import __main____main__.locktest1.lock != __main__.lock

したがって、問題に対する1つの修正(これは最善とはほど遠い)であり、何が起こっているかを確認するには、2つのスクリプトを次のように変更できます。

test1.py:

from __future__ import with_statement
from threading import Thread, RLock
import time

lock = RLock()

class Test1(object):
    def __init__(self):
        print("Start Test1")
        import test2    # <<<<<<<<<<<<<<<<<<<<<<<< Import is done here to be able to refer to __main__.lock.
        self.test2 = test2.Test2()
        self.__Thread = Thread(target=self.myThread, name="thread")
        self.__Thread.daemon = True
        self.__Thread.start()
        self.test1Method()

    def test1Method(self):
        print("start test1Method")
        with lock:
            print("entered test1Method")
            time.sleep(5)
            print("end test1Method")

    def myThread(self):
        self.test2.test2Method()

if __name__ == "__main__":
    client = Test1()
    raw_input()

test2.py:

from __future__ import with_statement
import time
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<< test1 is changed to __main__ to get the same lock as the one used in the launched script.
import __main__

lock = __main__.lock

class Test2(object):
    def __init__(self):
        print("Start Test2")

    def test2Method(self):
        print("start test2Method")
        with lock:
            print("entered test2Method")
            time.sleep(5)
            print("end test2Method")

HTH、

于 2013-01-21T15:17:56.723 に答える
1

printステートメントの前後にステートメントを使用し、importステートメントid(lock)が作成された直後に出力すると、実際には2 つのロックが作成されていることがわかります。モジュールが 2 回インポートされているようです。mouad は彼の回答で、これは がtest1.py最初に as としてインポートされ__main__、次に asとしてインポートされるためであると説明してtest1います。これにより、ロックが 2 回インスタンス化されます。

いずれにせよ、グローバルロックを使用することは良い解決策ではありません。より良い解決策がいくつかありますが、そのうちの 1 つがニーズに合っていることがわかると思います。

  • ロックを のクラス変数としてインスタンスTest1化し、引数として に渡します。Test2

  • Test1inの通常の変数としてロックをインスタンス__init__化し、引数として に渡しますTest2

  • if __name__ == "__main__"ブロックでロックをインスタンス化し、それをTest1に渡し、次に からTest1に渡しTest2ます。

  • if __name__ == "__main__"ブロックでロックをインスタンス化し、最初にロックでインスタンス化してから、インスタンスロックをTest2に渡します。(これは最も分離された方法であり、この方法を使用することをお勧めします。これにより、少なくとも単体テストが容易になります。)Test2Test1

最後の提案のコードは次のとおりです。

test1.py:

class Test1(object):
    def __init__(self, lock, test2):
        print("Start Test1")
        self.lock = lock
        self.test2 = test2
        self.__Thread = Thread(target=self.myThread, name="thread")
        self.__Thread.daemon = True
        self.__Thread.start()
        self.test1Method()

    def test1Method(self):
        print("start test1Method")
        with self.lock:
            print("entered test1Method")
            time.sleep(1)
            print("end test1Method")

    def myThread(self):
        self.test2.test2Method()

if __name__ == "__main__":
    lock = RLock()
    test2 = test2.Test2(lock)
    client = Test1(lock, test2)

test2.py:

class Test2(object):
    def __init__(self, lock):
        self.lock = lock
        print("Start Test2")

    def test2Method(self):
        print("start test2Method")
        with self.lock:
            print("entered test2Method")
            time.sleep(1)
            print("end test2Method")
于 2013-01-21T15:08:45.330 に答える
0

他の人が言ったように、問題は ではなく、threading循環インポートの特別なケースにあります。

なぜ特別なのですか?通常のワークフロー ( mod1importsmod2mod2imports mod1) は次のようになります。

  1. モジュール mod1 を使用したい場合は、それをインポートします ( import mod1)

  2. Python がそれを見つけると、インタープリターがそれを追加してsys.modulesコードの実行を開始します

  3. の行に到達するとimport mod2、 の実行を停止し、 の実行をmod1開始します。mod2

  4. インタープリターがimport mod1mod2に到達すると、既に追加されているためロードされませんmod1sys.modules

  5. その後 ( のコードがからの初期化されmod2ていないリソースにアクセスしない限りmod1) インタプリタはmod2との実行を終了しmod1ます。

しかし、あなたの場合はステップ 4.に!test1がないため、Python はもう一度実行されます。これは、そもそもインポートしたのではなく、コマンド ラインから実行したためです。test1sys.modules

したがって、サイクリック インポートは使用しないでください。

于 2013-01-21T15:35:01.533 に答える