本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie
1.GIL与线程调度
Python中的线程是操作系统的原生线程,Python虚拟机使用一个全局解释器锁(Global Interpreter Lock)来互斥线程对Python虚拟机的使用
为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥,所以引入了GIL。
GIL:在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响。
在调用任何Python C API之前,要先获得GIL
GIL缺点:多处理器退化为单处理器;优点:避免大量的加锁解锁操作
线程调度的两个问题:
1.何时挂起当前线程
Python在执行了N条指令之后,通过软件模拟了时钟中断,开始了线程调度机制
2.选择哪个等待的线程
Python借用了底层操作系统所提供的线程调度机制决定下一个进入解释器的线程。
2.Python Thread
Python中所提供的最基础的多线程机制的接口是thread module,用C实现
在thread module的基础上,Python提供了一个更高层的多线程机制接口,threading module
thread module中的多线程接口
static PyMethodDef thread_methods[] = {
{"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS,
start_new_doc},
{"start_new", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS,
start_new_doc},
{"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock,
METH_NOARGS, allocate_doc},
{"allocate", (PyCFunction)thread_PyThread_allocate_lock,
METH_NOARGS, allocate_doc},
{"exit_thread", (PyCFunction)thread_PyThread_exit_thread,
METH_NOARGS, exit_doc},
{"exit", (PyCFunction)thread_PyThread_exit_thread,
METH_NOARGS, exit_doc},
{"interrupt_main", (PyCFunction)thread_PyThread_interrupt_main,
METH_NOARGS, interrupt_doc},
{"get_ident", (PyCFunction)thread_get_ident,
METH_NOARGS, get_ident_doc},
{"_count", (PyCFunction)thread__count,
METH_NOARGS, _count_doc},
{"stack_size", (PyCFunction)thread_stack_size,
METH_VARARGS,
stack_size_doc},
{NULL, NULL} /* sentinel */
};
typedef void *PyThread_type_lock;
static PyThread_type_lock interpreter_lock = 0; //这就是GIL
static long main_thread = 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();
}
typedef struct NRMUTEX{
LONG owned; //指示GIL是否可用
DWORD thread_id;//获得GIL的线程id
HANDLE hevent;//Win32平台下Event这个内核对象
}NRMUTEX, *PNRMUTEX;
static struct key *
find_key(int key, void *value)
{
struct key *p, *prev_p;
//[1]:获取当前线程的线程id,并锁住线程状态对象链表
long id = PyThread_get_thread_ident();
PyThread_acquire_lock(keymutex, 1);
//[2]:遍历线程状态对象链表,寻找key和id都匹配的元素
for (p = keyhead; p != NULL; p = p->next) {
if (p->id == id && p->key == key)
goto Done;
}
//[3]:如果[2]处搜索失败,则创建新的元素,并加入线程状态对象链表
p = (struct key *)malloc(sizeof(struct key));
if (p != NULL) {
p->id = id;
p->key = key;
p->value = value;
p->next = keyhead;
keyhead = p;
}
Done:
//[4]:释放锁住的线程状态对象链表
PyThread_release_lock(keymutex);
return p;
}
//查询操作
void *PyThread_get_key_value(int key)
//插入操作
int PyThread_set_key_value(int key, void *value)
//删除操作
void PyThread_delete_key(int key)
/*Interpreter main loop*/
PyObject *PyEval_EvalFrameEx(PyFrameObject *f){
for(;;){
if (--_Py_Ticker < 0) {
//在切换线程之前,重置_Py_Ticker为100,为下一个线程做准备
_Py_Ticker = _Py_CheckInterval;
tstate->tick_counter++;
if (interpreter_lock) {
//[1]:撤销当前线程状态对象,释放GIL,给别的线程一个机会
PyThreadState_Swap(NULL)
PyThread_release_lock(interpreter_lock);
//[2]:别的线程现在已经开始执行了,咱们重新再申请GIL,等待下一次被调度
PyThread_acquire_lock(interpreter_lock, 1);
PyThreadState_Swap(tstate)
}
}
fast_next_opcode:
//...
}
}
PyEval_EvalFrameEx每执行一条字节码指令,_Py_Ticker就将减少1;当执行了_Py_CheckInterval条指令之后,
import thread
import time
input = None
lock = thread.allocate_lock() #创建一个Lock对象
def threadProc():
while True:
print 'sub thread id: ', thread.get_ident()
print 'sub thread %d wait lock...' % thread.get_ident()
lock.acquire()
print 'sub thread %d get lock...' % thread.get_ident()
print 'sub thread %d receive input : %s' % (thread.get_ident(), input)
print 'sub thread %d release lock...' % thread.get_ident()
lock.release()
time.sleep(1)
thread.start_new_thread(threadProc, ())
print 'main thread id : ', thread.get_ident()
while True:
print 'main thread %d wait lock...' % thread.get_ident()
lock.acquire() #线程在用户级需要访问共享资源之前需要先申请用户级的lock
print 'main thread %d get lock...' % thread.get_ident()
input = raw_input()
print 'main thread %d release lock...' % thread.get_ident()
lock.release() #释放lock
time.sleep(1)
在Win32平台下的Python实现中,用户级线程的互斥与同步机制是通过Event来完成的。
5.高级线程库——threading
import threading
import time
class MyThread(threading.Thread):
def run(self):
while True:
print 'sub thread : ', threading._get_ident()
time.sleep(1)
mythread = MyThread()
mythread.start()
while True:
print 'main thread : ', threading._get_ident()
time.sleep(1)