前言

GIL: 又称全局解释器锁. 作用是限制多线程同时执行, 保证同一时间内只有一个线程在执行. Python 由于 GIL 的存在, 导致在多核 CPU 上, 只能利用一个 CPU 的资源.

Python 自带的多线程在多核 CPU 上, 只对于 IO 密集型计算产生正面效果; 而当有至少有一个CPU密集型线程存在, 那么多线程效率会由于GIL而大幅下降.

之前写过一篇 C/C++和Python多线程初探-ubuntu 的博文, 那一篇是通过 ctypes 库引用 动态链接库 来实现真正的多线程。

ctypes 是 Python 的标准库, 只支持 C 语言, 多线程只能利用 pthread.h 实现, 它是基于 Linux 系统的, 无法做到跨平台.

C++11 引入了 thread 库, 它是基于 C++, 不基于平台. 而且 C++ 比 C 语言更加强大.

本博文的代码在 Linux 版本的 Visual Studio Code 上测试的.

操作系统:Ubuntu 20.04.4 LTS

参考文档

  1. std::thread

  2. pybind11

  3. Global Interpreter Lock (GIL)

配置环境

std::thread API: https://en.cppreference.com/w/cpp/thread/thread .

关于如何配置 C++ 和 Python 可以参考: C/C++和Python多线程初探-ubuntu .

Pybind11 是一个能够实现 C++11 与 Python 之间的无缝可操作性的库.

pybind11 — Seamless operability between C++11 and Python

pybind11 is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code.

pybind11 代码仓库: https://github.com/pybind/pybind11 .

pybind11 文档地址: https://pybind11.readthedocs.io/en/latest .

pybind11 文档 PDF 版本: https://pybind11.readthedocs.io/_/downloads/en/latest/pdf/ .

安装 pybind11

1
$ pip install pybind11

Python 实现真正的多线程

  1. 新建一个 foo.cpp 文件,粘贴下面的代码到文件中,保存文件( Ctrl+S )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <thread>
#include <pybind11/pybind11.h>

namespace py = pybind11;

void fn(int id) {
// io 操作不能并行, 因此注释掉 std::cout << id << std::endl;
while (1);
}

void create_thread() {
// release GIL
py::gil_scoped_release release;
std::thread t1(fn, 1);
std::thread t2(fn, 2);
std::thread t3(fn, 3);
std::thread t4(fn, 4);
t1.join();
t2.join();
t3.join();
t4.join();
}

PYBIND11_MODULE(thread, m) {
m.doc() = "test threads by pybind11 on Python.";
m.def("create_thread", &create_thread, "create a thread.");
}
  1. 新建一个 pybind11_test.py 文件,粘贴下面的代码到文件中,保存文件( Ctrl+S )
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python
# coding=utf-8
import time
import thread

if __name__ == '__main__':

thread.create_thread()
while True:
print("I am in Python!")
time.sleep(2)
  1. 编译成动态链接库.
1
2
3
4
$ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) foo.cpp -o thread$(python3-config --extension-suffix)
$ ls
foo.cpp pybind11_test.py thread.cpython-38-x86_64-linux-gnu.so
$
  1. 打开一个终端(运行 Python 脚本前)
1
2
3
4
# 安装 htop 命令
sudo apt-get install htop

htop

  1. 运行 pybind11_test.py 脚本 (可以在 htop 页面, 选中 Python 脚本进程按 F9 + Enter 结束运行)
1
$ python pybind11_test.py

可以发现 CPU 电脑上的四个核都被利用了,实现了 Python 真正的多线程。

结语

第五十三篇博文写完,开心!!!!

今天,也是充满希望的一天。