首先是问题的设定
+-------------+
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中的线程相关。
答案 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);
的来电。