Python多线程机制深入理解(2)

//创建bootstate结构
boot = PyMem_NEW(struct bootstate, 1);
if (boot == NULL)
return PyErr_NoMemory();
boot->interp = PyThreadState_GET()->interp;
boot->func = func;
boot->args = args;
boot->keyw = keyw;
boot->tstate = _PyThreadState_Prealloc(boot->interp);
if (boot->tstate == NULL) {
PyMem_DEL(boot);
return PyErr_NoMemory();
}
Py_INCREF(func);
Py_INCREF(args);
Py_XINCREF(keyw);
// 初始化多线程环境
PyEval_InitThreads();
//创建线程
ident = PyThread_start_new_thread(t_bootstrap, (void*) boot);
if (ident == -1) {
PyErr_SetString(ThreadError, "can't start new thread");
Py_DECREF(func);
Py_DECREF(args);
Py_XDECREF(keyw);
PyThreadState_Clear(boot->tstate);
PyMem_DEL(boot);
return NULL;
}
return PyInt_FromLong(ident);

1. 创建并初始化bootstate结构boot,在boot中,将保存关于Python的一切信息(线程过程,线程过程参数等)。

2. 初始化Python的多线程环境。

3. 以boot为参数,创建操作系统的原生线程。

从以上代码可以看出,Python在刚启动时,并不支持多线程,也就是说,Python中支持多线程的数据结构以及GIL都是没有创建的。当然这是因为大多数的Python程序都不需要Python的支持。

在Python虚拟机启动时,多线程机制并没有被激活,它只支持单线程,一旦用户调用thread.start_new_thread,明确的告诉Python虚拟机需要创建新的线程,这时Python意识到用户需要多线程的支持,这个时候,Python虚拟机会自动建立多线程需要的数据结构、环境以及GIL。

建立多线程环境

建立多线程环境,主要就是创建GIL。那么GIL是如何实现的呢?
打开"python/ceval.c":

static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */
static PyThread_type_lock pending_lock = 0; /* for pending calls */
static long main_thread = 0;

int
PyEval_ThreadsInitialized(void)
{
return interpreter_lock != 0;
}

void
PyEval_InitThreads(void)
{
if (interpreter_lock)
return;
interpreter_lock = PyThread_allocate_lock();
PyThread_acquire_lock(interpreter_lock, 1);
main_thread = PyThread_get_thread_ident();
}

在这段代码中,iterpreter_lock就是GIL。

无论创建多少个线程,Python建立多线程环境的动作只会执行一次。在创建GIL之前,Python会检查GIL是否已经被创建,如果是,则不再进行任何动作,否则,就会去创建这个GIL。

在上述代码中,我们可以看到,创建GIL使用的是Pythread_allocate_lock完成的,下面看看该函数的内部实现:

PyThread_type_lock
PyThread_allocate_lock(void)
{
PNRMUTEX aLock;

dprintf(("PyThread_allocate_lock called\n"));
if (!initialized)
PyThread_init_thread();

aLock = AllocNonRecursiveMutex() ;

dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock));

return (PyThread_type_lock) aLock;
}

可以看到该函数返回了alock,alock是结构体PNRMUTEX,实际上就是我们需要创建的那个interperter_lock(GIL)。这么说来,GIL就是结构体PNRMUTEX呀,于是我们找来它的真身:

typedef struct NRMUTEX {
LONG owned ;
DWORD thread_id ;
HANDLE hevent ;
} NRMUTEX, *PNRMUTEX ;

这里又三个变量,owned、thread_id和hevent。这里的hevent是windows平台下的Event这个内核对象,也就是通过Event来实现线程之间的互斥。thread_id将记录任一时刻获得GIL的线程的id。

那么owned是什么呢?

GIL中的owned是指示GIL是否可用的变量,它的值被初始化为-1,Python会检查这个值是否为1,如果是,则意味着GIL可用,必须将其置为0,当owned为0后,表示该GIL已经被一个线程占用,不可再用;同时,当一个线程开始等待GIL时,其owned就会被增加1;当一个线程最终释放GIL时,一定会将GIL的owned减1,这样,当所有需要GIL的线程都最终释放了GIL之后,owned将再次变为-1,意味着GIL再次变为可用。

关于Python中的多线程,今天我们就学到这里。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/2827ea2ce19a34ada646bb4c4917335c.html