PHP前端开发

驯服 Python 的 GIL 野兽:驾驭并发性的艺术

百变鹏仔 1个月前 (01-17) #Python
文章标签 野兽

python、GIL、并发性、多线程、多进程

Python 的全局解释器 (GIL) 是一个内置机制,它确保每次只有一个线程能够执行 Python 字节码。这个锁是为了防止数据损坏,因为它阻止了多个线程同时修改共享数据。

GIL 的限制

虽然 GIL 对于确保数据完整性至关重要,但它对 Python 的并发性也有重大限制:

立即学习“Python免费学习笔记(深入)”;

克服 GIL 的限制

虽然 GIL 无法完全绕过,但有一些技术可以减轻其对并发性的影响:

1. 多进程

多进程是使用多个操作系统进程而不是 Python 线程来实现并发的。由于每个进程都有自己的 GIL,因此它们可以同时执行而没有任何锁争用:

import multiprocessingdef task(num):print(f"Process {num}: {num * num}")if __name__ == "__main__":processes = [multiprocessing.Process(target=task, args=(i,)) for i in range(4)]for process in processes:process.start()for process in processes:process.join()

2. 多线程与队列

使用多线程和队列可以实现并行性,同时避免 GIL 争用。线程将任务放入队列,而其他线程从队列中获取任务并执行它们:

import threadingimport queuequeue = queue.Queue()def producer():for i in range(10):queue.put(i)def consumer():while not queue.empty():item = queue.get()print(f"Thread: {item * item}")threads = [threading.Thread(target=producer), threading.Thread(target=consumer)]for thread in threads:thread.start()for thread in threads:thread.join()

3. Greenlets

Greenlets 是协程,它们允许您在单个线程中暂停和恢复函数。由于 Greenlets 不受 GIL 的约束,因此它们可以在不发生锁争用的情况下实现并发:

import geventdef task(num):print(f"Greenlet {num}: {num * num}")gevent.joinall([gevent.spawn(task, i) for i in range(4)])

4. C/C++ 扩展

对于需要高性能的并发应用程序,可以编写 C/C++ 扩展并将其与 Python 集成。C/c++ 代码不受 GIL 的影响,因此可以提供更快的并行性:

#include <python.h>static PyObject* py_task(PyObject* self, PyObject* args) {int num;if (!PyArg_ParseTuple(args, "i", &amp;num)) {return NULL;}// 执行任务int result = num * num;return Py_BuildValue("i", result);}static PyMethodDef methods[] = {{"task", py_task, METH_VARARGS, "PerfORM a task in a C extension"},{NULL, NULL, 0, NULL}};static PyModuleDef module = {PyModuleDef_HEAD_INIT,"c_extension","C extension for parallel task execution",-1,methods};PyMODINIT_FUNC PyInit_c_extension(void) {return PyModule_Create(&amp;module);}</python.h>

总结

Python 的 GIL 虽然对于保证数据完整性至关重要,但它会限制并发性。通过采用多进程、多线程与队列、Greenlets 或 C/C++ 扩展等策略,您可以克服 GIL 的限制,释放 Python 并发性的全部潜力。不过,在使用这些技术时,需要仔细考虑它们的优点、缺点和适用性。