在多线程环境中Python< - > Cython< - > C之间的通信

时间:2015-03-24 22:14:08

标签: python c multithreading openmp cython

首先是问题的设定

+-------------+                                                 

   Python        P0                    P1    P1                 
                 +                     ^ +   ^ +                
+------------+   |                     | |   | |                
                 |                     + v   + v                
   Cython        +->C0                 C1    C1                 
                      +                ^ +   ^ +                
+------------+        |                | |   | |                
                      |           +----------+-v-----+          
   C                  +->F0... +--+    | |           +------>...
                                  +----+-v-----------+          
+------------+    

程序的入口点是Python函数P0,它通过Cython调用C函数F0(来自用户库)。此C函数使用OpenMP创建多个线程。每个线程都在cython中使用回调来调用python函数P1并行运行。这些P1函数返回一个值,将其返回到C代码中。问题是:如何使用内部C多线程的cython回调正确调用P1。

我现在非常了解Python中的GIL,以及来自Cython的with / nogil: http://docs.cython.org/src/userguide/external_C_code.html#acquiring-and-releasing-the-gil

还来自API: https://docs.python.org/2/c-api/init.html#non-python-created-threads

我在这里阅读的帖子只是单方面的沟通Python-> Cython或Cython-> Python或C语言调用Cython中的多线程问题。

因此,如何处理这种情况?

由于使用了Cython,因此将丢弃C的API。最可能的解决方案与Cython中的with gil / nogil选项有关。

我的想法是,nogil应该放在C0上,因为代码进入C库,而gil放在C1上。

C0(...) nogil
C1(...) with gil

返回图表

+-------------+                                                 

   Python        P0                    P1    P1                 
                 +                     ^ +   ^ +                
+------------+   |                     | |   | |                
                 |                     + v   + v                
   Cython        +->(C0) nogil        (C1    C1) with gil        
                      +                ^ +   ^ +                
+------------+        |                | |   | |                
                      |           +----------+-v-----+          
   C                  +->F0... +--+    | |           +------>...
                                  +----+-v-----------+          
+------------+                                                  

然而,我的经验是P1函数之一永远不会正确返回到Cython并且代码仍然无法等待P1返回。

从P1返回时,代码被卡住的正确答案或解释是什么?

ADD1:代码是一个测试示例,可以与一个线程完美配合(或者将OpenMP设置为一个线程)。当不使用Python / Cython接口时,多线程测试也在工作,纯粹是一个C / C ++示例。因此,问题与GIL相关或与Python / Cython中的线程相关。

2 个答案:

答案 0 :(得分:1)

我确定其他人可以确切地告诉你如何做到这一点,但对于这种类型的问题,我发现通常很容易将事情分解成更简单的部分。找出一种方法,通过回调从Python调用Cython到C,然后调用回调到Cython和Python。使用简单的测试程序执行此操作,然后在完成此操作后,引入OpenMP,然后将您的技术应用于您正在使用的真实库。

此外,如果你遇到困难,也许你可以通过将回调移动到Cython或C层来解决问题,传递足够的参数来完成它的工作。这可能是不可能的,具体取决于您的应用程序,但它可能允许您取得进展。它甚至可能表现更好。如果您正在使用OpenMP,那么性能显然很重要,从回调调用中删除语言交叉代码意味着在线程级别的工作量减少,这将提高性能。

答案 1 :(得分:1)

检查生成的源后,似乎是声明

C1() with gil

以微妙的方式有所不同。也就是说,C0() nogil 肯定在调用时获取GIL,而with nogil: 允许包装在C0()上下文中没有生成Cython编译错误。但是,除非您明确添加此上下文,否则Cython通常会在输入C1之前继续获取GIL。这会导致死锁:C0等待获取由其封闭函数C0持有的锁,但在C0()返回之前不会释放锁。尝试使用with nogil: C0()替换var cy = window.cy = cytoscape({ container: document.getElementById('cy'), elements: { nodes: [{ data: { id: 'n0', batchID: 'b0' }, classes: '.original_nodes' }, { data: { id: 'n1', batchID: 'b0' }, classes: '.original_nodes' }, { data: { id: 'n2', batchID: 'b0' }, classes: '.original_nodes' }], edges: [{ data: { id: 'e0', batchID: 'b0', source: 'n0', target: 'n1' }, classes: '.orginal_edges' }, { data: { id: 'e1', batchID: 'b0', source: 'n1', target: 'n2' }, classes: '.orginal_edges' }] }, style: [{ // This is now essentially your default for the graph selector: 'node', style: { 'background-color': '#666', 'label': 'data(id) } }, { // This is for the original batch selector: '.original_nodes', style: { 'background-color': '#666', 'label': 'data(id) } }], layout: { name: 'grid' } ); // Add new data here -- potentially the result of an Ajax call. // Also, might want to reformat the array to be better ingested and more explicit about which ones are nodes or edges var newarray = [{ group: 'nodes', classes: '.b1_nodes', data: { id: 'n0', batchID: 'b1' } }, { group: 'nodes', classes: '.b1_nodes', data: { id: 'n8', batchID: 'b1' } }, { group: 'nodes', classes: '.b1_nodes', data: { id: 'n9', batchID: 'b1' } }, { group: 'edges', classes: '.b1_edges', data: { id: 'e2', batchID: 'b1', source: 'n8', target: 'n9' } }]; // Now, before we add the new data, let's create the new selector cy.style().selector('.b1_nodes').style({ 'background-color': 'blue' }).update(); cy.add(newarray); 的来电。