首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >python源码阅读笔记之线程机制

python源码阅读笔记之线程机制

作者头像
哒呵呵
发布2018-08-06 15:40:55
发布2018-08-06 15:40:55
8850
举报
文章被收录于专栏:鸿的学习笔记鸿的学习笔记
代码语言:javascript
复制
六,python的线程机制
GIL锁的机制,来源于python的内存管理和为了实现多线程,对共享内存资源的互斥实现。
当然,python对进程的支持很好,这在linux下,很有比线程更好的使用,因为在linux里没有线程的概念,
有着的是轻量级的进程以及pipeline等进程间通信。
如果非要使用线程,解释器只有一个,导致的各种线程必须要获得字节码解释器,也就是GIL。
有两个核心问题:在何时挂起当前线程,选择下一个线程?在众多等待的线程中选择其中一个?
对于第一个问题,python通过执行的字节码指令弄的,如下:
import sys

sys.getcheckinterval()

Out[2]: 100

100条指令便会切换下一个。
至于第二个问题:由底层的操作系统决定,因为python的线程实际上是将obj这些直接调用的Event指令。

在thread模块中,有如下的方法:
static PyMethodDef lock_methods[] = {
    {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
     METH_VARARGS, acquire_doc},
    {"acquire",      (PyCFunction)lock_PyThread_acquire_lock,
     METH_VARARGS, acquire_doc},
    {"release_lock", (PyCFunction)lock_PyThread_release_lock,
     METH_NOARGS, release_doc},
    {"release",      (PyCFunction)lock_PyThread_release_lock,
     METH_NOARGS, release_doc},
    {"locked_lock",  (PyCFunction)lock_locked_lock,
     METH_NOARGS, locked_doc},
    {"locked",       (PyCFunction)lock_locked_lock,
     METH_NOARGS, locked_doc},
    {"__enter__",    (PyCFunction)lock_PyThread_acquire_lock,
     METH_VARARGS, acquire_doc},
    {"__exit__",    (PyCFunction)lock_PyThread_release_lock,
     METH_VARARGS, release_doc},
    {NULL}              /* sentinel */
};

想必你也注意到了,有很多重复的。

线程的创建:
static PyObject *
thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
{
    PyObject *func, *args, *keyw = NULL;
    struct bootstate *boot;
    long ident;

    if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3,
                           &func, &args, &keyw))
        return NULL;
    if (!PyCallable_Check(func)) {
        PyErr_SetString(PyExc_TypeError,
                        "first arg must be callable");
        return NULL;
    }
    if (!PyTuple_Check(args)) {
        PyErr_SetString(PyExc_TypeError,
                        "2nd arg must be a tuple");
        return NULL;
    }
    if (keyw != NULL && !PyDict_Check(keyw)) {
        PyErr_SetString(PyExc_TypeError,
                        "optional 3rd arg must be a dictionary");
        return NULL;
    }
    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(); /* Start the interpreter's thread-awareness */
    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);
}

检查参数,创建并初始化bootstate结构的boot,这里保存着线程的各种信息。
初始化线程
以boot为参数创建操作系统的原生参数

好了,想必有很多疑问吧,看看GIL在python里到底怎么实现的吧。

static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */

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();
}

如下所示:这个是具有平台相关性的,在python文件夹里面放了各种thread_*.h

PyThread_allocate_lock(void)
{
    PNRMUTEX aLock;
    if (!initialized)
        PyThread_init_thread();

    aLock = AllocNonRecursiveMutex() ;

    return (PyThread_type_lock) aLock;
}

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

BOOL
InitializeNonRecursiveMutex(PNRMUTEX mutex)
{
    mutex->owned = -1 ;  /* No threads have entered NonRecursiveMutex */
    mutex->thread_id = 0 ;
    mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
    return mutex->hevent != NULL ;      /* TRUE if the mutex is created */
}

从这里可以看出这个是原生的windows系统的Event,thread_id记录着启动的线程

接下来获取lock
int
PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
{
    int success ;

    dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));

    success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;

    dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));

    return success;
}

void
PyThread_release_lock(PyThread_type_lock aLock)
{
    dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));

    if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
        dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError()));
}
通过参数waitflag来判别,是否是等待的

通过之前的owned判别是否获得GIL


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鸿的学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档