5

我使用线程锁编写了一个简单的测试程序。这个程序没有按预期运行,python 解释器也没有抱怨。

测试1.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()

测试2.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__而不是test1,因此,如果您想获得在启动脚本中定义的锁,您不应该,import test1但您应该import __main__这样做,因为如果您执行第一个,您将创建另一个不同于__main__.lock( test1.lock != __main__.lock) 的锁。

因此,可以解决您的问题(远非最佳)并查看发生了什么,您可以将 2 脚本更改为:

测试1.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()

测试2.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")

高温下,

于 2013-01-21T15:17:56.723 回答
1

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

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

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

  • 将锁实例化为 in 的普通变量Test1__init__并将其作为参数传递给Test2

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

  • 实例化if __name__ == "__main__"Test2中的锁,先用锁实例化,然后将Test2实例锁传递给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

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,而在于您的循环导入的特殊情况。

为什么特别?因为通常的工作流程(mod1导入mod2mod2导入mod1)如下所示:

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

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

  3. 当它达到与 一致时import mod2,它停止执行mod1并开始执行mod2

  4. 当解释器到达import mod1withinmod2时,它不会加载mod1,因为它已经添加到sys.modules

  5. 之后(除非某些代码从mod2访问一些未初始化的资源mod1)解释器完成了mod2和的执行mod1

但是在您的情况下,在第 4 步。Pythontest1再执行一次,因为没有test1in sys.modules!原因是您一开始没有导入它,而是从命令行运行它。

所以,不要使用循环导入——正如你所看到的那样,这真是一团糟。

于 2013-01-21T15:35:01.533 回答