python的多线程由于GIL的限制,无法使用多核,如果想使用多核就需要用到多进程,但多进程资源消耗巨大,所以比较合理的方法是在计算密集型任务上使用C/C++来实现构建python模块。
下面的示例利用了pybind11把c++函数封装成python类,然后再使用python来调用模块。
1.C++代码,指定pybind11来绑定C++的函数,这里绑定了thread_task和perform_multithreading两个函数
// example.cpp
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <thread>
#include <vector>
#include <iostream>
namespace py = pybind11;
// 一个简单的多线程任务,用于模拟长时间运行的情况
void thread_task(int id, int iterations, py::function callback) {
// 在这里执行需要关闭 GIL 的多线程任务
int sum = 1;
for (int i = i; i < iterations; ++i) {
// 模拟一些计算或其他密集型任务
sum=sum*i;
}
// 必须获取全局解释器锁才能执行py::function的callback函数
py::gil_scoped_acquire acquire;
callback(id, "Thread task completed");
}
void perform_multithreading(int num_threads, int iterations, py::function callback) {
// 关闭全局解释器锁
py::gil_scoped_release gil;
// 创建线程组
std::vector<std::thread> threads;
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back(thread_task, i, iterations, callback);
}
// 等待所有线程完成
for (auto &t : threads) {
t.join();
}
}
PYBIND11_MODULE(example, m) {
m.def("perform_multithreading", &perform_multithreading, "Perform multithreading with GIL released");
m.def("thread_task",&thread_task,"single task");
}
2.编写setup.py利用setuptools和pybind11构建模块,这里需要确保你的环境已经安装了pybind11,可以使用”pip install pybind11“来安装。另外如果是windows电脑需要安装visual stido的C+/C++相关的模块(MSVC/C++CL支持/win10sdk等),安装中如果有其它问题请自行查找原因
# setup.py
import setuptools
from setuptools import setup, Extension
import os
import sys
import platform
import pybind11
# 修改为你自己的Pybind11路径
pybind11_path = pybind11.get_include()
# 根据不同平台设置不同的编译参数
extra_compile_args = []
if platform.system() == 'Windows':
extra_compile_args.append('/std:c++latest')
ext_modules = [
Extension(
'example',
['example.cpp'],
include_dirs=[pybind11_path],
language='c++',
extra_compile_args=extra_compile_args
)
]
# 执行setup
setup(
name='example',
version='0.0.1',
author='Your Name',
description='A simple example of using Pybind11 with Setuptools on Windows',
ext_modules=ext_modules,
)
执行下面命令可得到example.so或者example.pyd的动态链接库
python setup.py build develop
3. 使用测试样例测试效果
# test.py
import example
import time
import threading
from multiprocessing.dummy import Pool
def my_callback(id, message):
pass
#print(f"Thread {id} says: {message}\n")
def sum_thread(iterations,call_back):
tid = threading.current_thread().ident
example.thread_task(tid,iterations,my_callback)
t0 = time.time()
example.perform_multithreading(100, 5000000, my_callback)
print('C++多线程耗时:',time.time()-t0)
params = [5000000]*100
t0 = time.time()
with Pool(100) as run_pool:
run_pool.map(lambda x:sum_thread(x,my_callback),params)
print('python多线程耗时:',time.time()-t0)
这里用了C++的多线程和python的多线程计算了100遍4999999!,我们可以看下消耗的时间:
python test.py
C++多线程耗时: 0.015626907348632812
python多线程耗时: 0.11573576927185059
实践证明使用C++多线程关闭GIL,在这个计算型任务上得到了进10倍的加速。