【问题标题】:Synchronizing embedded Python in multi-threaded program在多线程程序中同步嵌入式 Python
【发布时间】:2011-05-08 09:03:42
【问题描述】:

下面是在多线程程序中使用 Python 解释器的例子:

#include <python.h>
#include <boost/thread.hpp>

void f(const char* code)
{
    static volatile auto counter = 0;
    for(; counter < 20; ++counter)
    {
        auto state = PyGILState_Ensure();
        PyRun_SimpleString(code);
        PyGILState_Release(state);

        boost::this_thread::yield();
    }
}

int main()
{
    PyEval_InitThreads();
    Py_Initialize();
    PyRun_SimpleString("x = 0\n");
    auto mainstate = PyEval_SaveThread();

    auto thread1 = boost::thread(f, "print('thread #1, x =', x)\nx += 1\n");
    auto thread2 = boost::thread(f, "print('thread #2, x =', x)\nx += 1\n");
    thread1.join();
    thread2.join();

    PyEval_RestoreThread(mainstate);
    Py_Finalize();
}

看起来不错,但不同步。 Python 解释器在 PyRun_SimpleString 期间多次释放和重新获取 GIL(参见 docs, p.#2)。

我们可以使用自己的同步对象序列化 PyRun_SimpleString 调用,但这是错误的方式。

Python 有自己的同步模块 - _threadthreading。但它们在这段代码中不起作用:

Py_Initialize();
PyRun_SimpleString(R"(
import _thread
sync = _thread.allocate_lock()

x = 0
)");

auto mainstate = PyEval_SaveThread();

auto thread1 = boost::thread(f, R"(
with sync:
    print('thread #1, x =', x)
    x += 1
)");
  • 它会产生错误File "&lt;string&gt;", line 3, in &lt;module&gt; NameError: name '_[1]' is not defined 和死锁。

如何同步嵌入的python代码最高效?

【问题讨论】:

  • 你期望什么输出?
  • @Sven Marnach :感谢您的评论,更新了问题。

标签: c++ python c multithreading


【解决方案1】:

当 CPython 调用可能阻塞(或重新进入 Python)的函数时,它会在调用函数之前释放全局解释器锁,然后在函数返回后重新获取锁。在您的代码中,您调用内置的print 函数会导致解释器锁被释放并运行另一个线程(请参阅stringobject.c 中的string_print)。

所以你需要自己的锁:全局解释器锁不适合确保执行 I/O 的 Python 代码的序列化。

由于您使用的是 Boost 线程框架,您可能发现使用 Boost thread synchronization primitives 之一不是最方便的,例如boost::interprocess::interprocess_mutex.

[编辑:我原来的答案是错误的,正如 Abyx 所指出的那样。]

【讨论】:

  • 不,这只意味着 PyGILState_Ensure 会阻塞,直到 GIL 被释放。查看它的源代码,它调用了 PyThread_acquire_lock。
  • 你是对的,它调用PyEval_RestoreThread。让我再试一次。
【解决方案2】:

with 语句在 Python 3.1 中具有 issue,但在 Python 3.2 和 Python 2.7 中已修复。

所以正确的解决方案是使用threading模块进行同步。

为避免此类问题,不应使用在全局字典中使用临时变量的多线程代码,或为每个线程使用不同的全局字典。

【讨论】:

    猜你喜欢
    • 2012-05-24
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    • 2018-07-29
    • 2012-05-01
    • 1970-01-01
    • 2021-03-28
    相关资源
    最近更新 更多