【问题标题】:Deadlock only when running Cython compiled .py but not when running from interpreter仅在运行 Cython 编译的 .py 时出现死锁,但从解释器运行时不会出现死锁
【发布时间】:2021-01-21 13:53:39
【问题描述】:

以下示例代码在从解释器运行时按预期运行。但是,当我对示例进行 cythonize 然后导入已编译的模块时,我遇到了死锁。

你知道这种行为的原因是什么吗?我可以以某种方式解决吗?是否有可能实现超时,以便线程在一定时间后终止?

from concurrent import futures
import time

class StopFlag:
    def __init__(self):
        self._started = 0
    
    @property
    def started(self):
        return self._started
        
    def stop(self):
        self._started = 0
        
    def start(self):
        self._started = 1
        
    
def my_loop(stop_flag):
        total_records = 0

        print ("Started loop")
        # wait until measurement is running actively
        while not stop_flag.started:
            pass
        
        print ("Started measurement")
        # wait until measurement is over            
        while stop_flag.started:
            pass
        
        print ("Measurement over")


stop_flag = StopFlag()

with futures.ThreadPoolExecutor() as t:
    t.submit(my_loop, stop_flag)

    time.sleep(1)
    stop_flag.start()
    time.sleep(1)
    stop_flag.stop()

解释:

$ py -3.6 treading.py
Started loop
Started measurement
Measurement over

Cythonized:

$ cythonize -ai3 treading.py
Compiling C:\mydir\Trials\treading.py because it changed.
[1/1] Cythonizing C:\mydir\Trials\treading.py
running build_ext
building 'treading' extension
creating C:\mydir\Trials\tmpcu1r5lge\Release
creating C:\mydir\Trials\tmpcu1r5lge\Release\mydir
creating C:\mydir\Trials\tmpcu1r5lge\Release\mydir\Trials
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29333\bin\HostX86\x64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MT "-Ic:\program files (x86)\microsoft visual studio\shared\python36_64\include" "-Ic:\program files (x86)\microsoft visual studio\shared\python36_64\include" "-IC:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29333\ATLMFC\include" "-IC:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29333\include" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\ucrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\shared" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\um" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\winrt" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\cppwinrt" /TcC:\mydir\Trials\treading.c /FoC:\mydir\Trials\tmpcu1r5lge\Release\mydir\Trials\treading.obj
treading.c
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29333\bin\HostX86\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /nodefaultlib:libucrt.lib ucrt.lib /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO "/LIBPATH:c:\program files (x86)\microsoft visual studio\shared\python36_64\libs" "/LIBPATH:c:\program files (x86)\microsoft visual studio\shared\python36_64\PCbuild\amd64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29333\ATLMFC\lib\x64" "/LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.28.29333\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\ucrt\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\um\x64" /EXPORT:PyInit_treading C:\mydir\Trials\tmpcu1r5lge\Release\mydir\Trials\treading.obj /OUT:C:\mydir\Trials\treading.cp36-win_amd64.pyd /IMPLIB:C:\mydir\Trials\tmpcu1r5lge\Release\mydir\Trials\treading.cp36-win_amd64.lib
   Bibliothek "C:\mydir\Trials\tmpcu1r5lge\Release\mydir\Trials\treading.cp36-win_amd64.lib" und Objekt "C:\mydir\Trials\tmpcu1r5lge\Release\mydir\Trials\treading.cp36-win_amd64.exp" werden erstellt.
Code wird generiert.
Codegenerierung ist abgeschlossen.

$ py -3.6 -c "import treading;"
Started loop
<< deadlock >>

这是future.submit调用的编译代码:

+38: with futures.ThreadPoolExecutor() as t:
+39:     t.submit(my_loop, stop_flag)
          __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_t); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 39, __pyx_L6_error)
          __Pyx_GOTREF(__pyx_t_2);
          __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_submit); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 39, __pyx_L6_error)
          __Pyx_GOTREF(__pyx_t_3);
          __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
          __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_my_loop); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 39, __pyx_L6_error)
          __Pyx_GOTREF(__pyx_t_2);
          __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_n_s_stop_flag); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 39, __pyx_L6_error)
          __Pyx_GOTREF(__pyx_t_1);
          __pyx_t_8 = PyTuple_New(2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 39, __pyx_L6_error)
          __Pyx_GOTREF(__pyx_t_8);
          __Pyx_GIVEREF(__pyx_t_2);
          PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_2);
          __Pyx_GIVEREF(__pyx_t_1);
          PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_1);
          __pyx_t_2 = 0;
          __pyx_t_1 = 0;
          __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_3, __pyx_t_8, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 39, __pyx_L6_error)
          __Pyx_GOTREF(__pyx_t_1);
          __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
          __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
          __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

【问题讨论】:

    标签: python-3.x deadlock concurrent.futures cythonize


    【解决方案1】:

    免责声明 - 我对 cython 一无所知,我今天第一次尝试。这个“答案”并没有真正回答问题。

    我在 Linux 上对此进行了测试,但也存在问题。代码的行为就像使用协作多任务处理一样。如果我添加到StopFlag.started 一个time.sleep(0),或者只打开一个文件(不读取它),那么它可以工作。可能有用的解决方法。

    编辑 2: 似乎与 GIL 相关。下面的更改有帮助(至少在我的系统上)。

    class StopFlag:
         @property
         def started(self):
    +        with nogil:
    +            pass
             return self._started
    

    所以我猜,运行 my_loop 的线程一直在持有 GIL(它在我的系统上使用 100% 的 CPU),而主线程没有让 GIL 能够真正改变 stop_flag 值。

    要检查这一点,第二个更改:

    with futures.ThreadPoolExecutor() as t:
        t.submit(my_loop, stop_flag)
    
        time.sleep(1)
        print("before stop_flag.start")
        stop_flag.start()
        print("after stop_flag.start")
        time.sleep(1)
        stop_flag.stop()
    

    我仍然只能在终端上:

    python -c "import treading;"
    Started loop
    
    

    t.submit之后,主线程确实执行了任何其他代码行,并且before stop_flag.start没有被打印出来。

    【讨论】:

    • 我实际上无法理解您的解释,因为代码似乎挂在 t.submit 上,而不是在 while 循环之一。不过,感谢您尝试解释这种行为。
    • 100% cpu 很糟糕!我现在看到解释器每次运行 while 循环都需要一些时间,并将一些 cpu 时间留给其他线程。另一方面,编译后的代码是如此之快,以至于它紧紧地运行着循环。我如何实现while循环只是我的一个坏主意。我应该在 while 身体中使用 time.sleep(smalltime)。我现在再次对此进行了测试,事实证明它是造成混乱的根源。所以在使用 Cython 时要小心紧密的循环。我再说一遍:这只是个坏主意!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-21
    • 1970-01-01
    • 1970-01-01
    • 2022-11-05
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    相关资源
    最近更新 更多