在多线程程序中同步嵌入式Python

2024-01-07

以下是在多线程程序中使用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(请参阅文档,第#2页 http://docs.python.org/py3k/c-api/init.html#thread-state-and-the-global-interpreter-lock).

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

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

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 "<string>", line 3, in <module> NameError: name '_[1]' is not defined和僵局。

如何最有效地同步嵌入式Python代码?


当CPython调用一个可能阻塞(或重新进入Python)的函数时,它会在调用该函数之前释放全局解释器锁,然后在函数返回后重新获取锁。在您的代码中,这是您对内置函数的调用print导致解释器锁被释放并且另一个线程运行的函数(参见string_print in 字符串对象.c https://svn.python.org/projects/python/branches/release27-maint/Objects/stringobject.c).

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

由于您使用的是 Boost 线程框架,因此您可能会发现使用其中一种 Boost 并不是最方便的线程同步原语 http://www.boost.org/doc/libs/1_37_0/doc/html/interprocess/synchronization_mechanisms.html, e.g. boost::interprocess::interprocess_mutex.

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在多线程程序中同步嵌入式Python 的相关文章

随机推荐