使用python线程锁和circulair导入时出现意外行为

时间:2013-01-21 14:46:48

标签: python multithreading locking

我使用线程锁编写了一个简单的测试程序。该程序的行为不符合预期,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中导入它时,一切正常。

可能它与circulair进口有关。

但为什么python不抱怨这个?

3 个答案:

答案 0 :(得分:3)

在Python中使用$ python test1.py执行python脚本时,您的test1.py将被导入为__main__而不是test1,所以如果你想获得在启动的脚本中定义的锁不应该import test1,但是您应该import __main__,因为如果您执行第一个锁定,则会创建另一个与__main__.lock不同的锁({1} }})。

因此,您可以解决问题(远远不是最好的)和查看正在发生的事情,您可以将2脚本更改为:

test1.py:

test1.lock != __main__.lock

test2.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()

HTH,

答案 1 :(得分:1)

print语句之前和之后使用import语句并在创建后立即打印id(lock)表明实际上已创建了两个锁。似乎模块被导入了两次,并且mouad在他的回答中解释说这是因为test1.py首先导入__main__然后导入test1,这导致锁被实例化两次。

尽管如此,使用全局锁定无论如何都不是一个好的解决方案。有几种更好的解决方案,我认为你会发现其中一种能满足你的需求。

  • 将锁实例化为Test1的类变量,并将其作为参数传递给Test2

  • 将锁实例化为Test1__init__的正常变量,并将其作为参数传递给Test2

  • if __name__ == "__main__"块中实例化锁定并将其传递给Test1,然后从Test1传递到Test2

  • if __name__ == "__main__"块中实例化锁定并首先使用锁定实例化Test2,然后将Test2实例锁定传递给{ {1}}。 (这是这种做法最脱钩的方式,我建议采用这种方法。至少可以简化单元测试。)。

以下是最后一条建议的代码:

Test1

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

答案 2 :(得分:0)

正如其他人所说,问题不在于threading,而是在你的循环导入的特殊情况下。

为什么特别?因为通常的工作流程(mod1导入mod2mod2导入mod1)看起来像下一个:

  1. 您想使用模块mod1,导入它(import mod1

  2. 当Python找到它时,解释器将其添加到sys.modules并开始执行代码

  3. 当它与import mod2对齐时,会停止执行mod1并开始执行mod2

  4. 当解释程序在import mod1内到达mod2时,加载mod1,因为它已添加到sys.modules < / p>

  5. 之后(除非mod2中的某些代码访问mod1中的某些未初始化资源)解释程序完成mod2mod1的执行。

    < / LI>

    但是在第4步的情况下,Python再次执行test1,因为test1中没有sys.modules!原因是您没有首先导入它,而是从命令行运行它。

    所以,只是不要使用循环导入 - 因为你看它真是一团糟。