Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python多线程多解释器C API_Python_Multithreading_Python C Api - Fatal编程技术网

Python多线程多解释器C API

Python多线程多解释器C API,python,multithreading,python-c-api,Python,Multithreading,Python C Api,我正在玩弄Python的C API,但是理解一些特殊情况是相当困难的。我可以测试它,但它似乎是一个容易出现错误和耗时的问题。所以我来这里是想看看是否有人已经这么做了 问题是,在线程和子解释器之间没有直接关系的情况下,使用子解释器管理多线程的正确方法是什么 Py_Initialize(); PyEval_InitThreads(); /* <-- needed? */ _main = PyEval_SaveThread(); /* <-- acquire lock? does it m

我正在玩弄Python的C API,但是理解一些特殊情况是相当困难的。我可以测试它,但它似乎是一个容易出现错误和耗时的问题。所以我来这里是想看看是否有人已经这么做了

问题是,在线程和子解释器之间没有直接关系的情况下,使用子解释器管理多线程的正确方法是什么

Py_Initialize();
PyEval_InitThreads(); /* <-- needed? */
_main = PyEval_SaveThread(); /* <-- acquire lock? does it matter? */
/* maybe do I not need it? */
i1 = Py_NewInterpreter();
i2 = Py_NewInterpreter();
螺纹2(几乎相同)

Thread3(几乎相同,但使用子解释器
i2

这是正确的吗?这是我想要实现的一般情况吗?有比赛条件吗


谢谢

Python中的子解释器没有很好的文档记录,甚至没有很好的支持。以下是我最大的理解。它在实践中似乎效果很好

在Python中处理线程和子解释器时,有两个重要的概念需要理解。首先,Python解释器不是真正的多线程解释器。它有一个全局解释器锁(GIL),执行几乎任何Python操作都需要它(这个规则有一些罕见的例外)

其次,线程和子解释器的每个组合都必须有自己的线程状态。解释器为它管理的每个线程创建一个线程状态,但是如果您想从不是由解释器创建的线程使用Python,则需要创建一个新的线程状态

首先,您需要创建子口译员:

初始化Python

Py_Initialize();
初始化Python线程支持

如果您计划从多个线程调用Python,则需要)。此呼叫还获得GIL

PyEval_InitThreads();
保存当前线程状态

我本可以使用
PyEval\u SaveThread()
,但它的一个副作用是释放GIL,然后需要重新获取GIL

PyThreadState*\u main=PyThreadState\u Get();
创建子口译员

PyThreadState*ts1=Py_new解释器();
PyThreadState*ts2=Py_new解释器();
恢复主解释器线程状态

PyThreadState\u交换(\u main);
我们现在有两个线程状态用于子解释器。这些线程状态仅在创建它们的线程中有效。想要使用其中一个子解释器的每个线程都需要为线程和解释器的组合创建一个线程状态

使用来自新线程的子解释器

下面是一个示例代码,用于在不是由子解释器创建的新线程中使用子解释器。新线程必须获取GIL,为线程创建新的线程状态,并重新组合,使其成为当前线程状态。最后,必须进行相反的清理

void do_stuff_in_thread(py解释器状态*interp)
{
//获得GIL
PyEval_AcquireLock();
//为子解释器interp创建新的线程状态
PyThreadState*ts=PyThreadState_New(interp);
//使ts成为当前线程状态
PyThreadState_交换(ts);
//在这一点上:
//1.你有GIL
//2.您拥有正确的线程状态—interp上下文中的新线程状态(此线程不是由python创建的)
//PYTHON在这里工作
//释放ts
PyThreadState_交换(空);
//清除并删除ts
PyThreadState_Clear(ts);
PyThreadState_Delete(ts);
//释放GIL
PyEval_ReleaseLock();
}
使用来自新线程的子解释器(后Python 3.3版)

先前的
do\u stuff\u in\u thread()
仍然适用于所有当前的Python版本。但是,Python3.3不推荐使用
PyEval\u AcquireLock()
/
PyEval\u ReleaseLock()
,这导致了一些难题

唯一记录在案的释放GIL的方法是调用
PyEval\u ReleaseThread()
PyEval\u SaveThread()
,这两种方法都需要线程状态,而清除和删除当前线程状态需要保持GIL。这意味着可以释放GIL或清除线程状态,但不能同时释放两者

幸运的是,有一个解决方案——删除当前线程状态,然后释放GIL。[此API仅在3.9之后才有文档记录,但它至少在Python 2.7之后就存在了]

此修改的
do\u stuff\u in\u thread()
也适用于所有当前的Python版本

void do_stuff_in_thread(py解释器状态*interp)
{
//为子解释器interp创建新的线程状态
PyThreadState*ts=PyThreadState_New(interp);
//使其成为当前线程状态并获取GIL
皮耶瓦尔(ts);
//在这一点上:
//1.你有GIL
//2.您拥有正确的线程状态—interp上下文中的新线程状态(此线程不是由python创建的)
//PYTHON在这里工作
//清除ts
PyThreadState_Clear(ts);
//删除当前线程状态并释放GIL
PyThreadState_DeleteCurrent();
}
现在,每个线程都可以执行以下操作:

线程1

_save = PyThreadState_Swap(i1);
  // python work 
PyThreadState_Restore(_save);
do_stuff_in_thread(ts1->interp);
螺纹2

do_stuff_in_thread(ts1->interp);
Thread3

do_stuff_in_thread(ts2->interp);
调用
Py\u Finalize()
将销毁所有子口译员。或者,可以手动销毁它们。这需要在主线程中完成,使用创建子解释器时创建的线程状态。最后,将主解释器线程状态设置为当前状态

//使ts1成为当前线程状态
PyThreadState_交换(ts1);
//摧毁口译员
Py_端解释器(ts1);
//使ts2成为当前线程状态
PyThreadState_交换(ts2);
_save = PyThreadState_Swap(i1);
  // python work 
PyThreadState_Restore(_save);
_save = PyThreadState_Swap(i2);
  // python work 
PyThreadState_Restore(_save);