C++11实现跨平台线程池
线程池:是一种线程的使用模式,它为了降低线程使用中频繁的创建和销毁所带来的资源消耗与代价。
通过创建一定数量的线程,让他们时刻准备就绪等待新任务的到达,而任务执行结束之后再重新回来继续待命。
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <condition_variable>
#include <queue>
#include<functional>
class ThreadPool {
public:
ThreadPool(int numThread) :stop(false){
for (int i=0;i<numThread; i++){
//lambda表达式
threads.emplace_back([this] {
while (true){
std::unique_lock<std::mutex> lock(mtx);
condition.wait(lock, [this] {
return !tasks.empty() || stop;
});
if (stop && tasks.empty()) {
return;
}
//取任务
std::function<void()> task(std::move(tasks.front()));
tasks.pop();
lock.unlock();
//完成任务
task();
}
});
}
}
~ThreadPool(){
{
std::unique_lock<std::mutex> lock(mtx);
stop = true;
}
//通知队列所有任务取完
condition.notify_all();
//让所有任务执行完成
for (auto& t : threads){
t.join();
}
}
//加任务
template<typename F, typename... Args>
void enqueue(F&& f, Args&&... args)
{
//函数绑定
std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
{
std::unique_lock<std::mutex> lock(mtx);
tasks.emplace(std::move(task));
}
//通知线程
condition.notify_one();
}
private:
std::vector<std::thread> threads;
std::queue<std::function<void()>> tasks;
//线程池符合生产消费者模式
std::mutex mtx;
std::condition_variable condition;
bool stop;
};
int main()
{
ThreadPool pool(4);
//加任务
for (int i=0; i<8; i++)
{
pool.enqueue([i] {
std::cout << "Task " << i << " is running in thread " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Task " << i << " is done" << std::endl;
});
}
return 0;
}
std::unique_lock
cpp
std::unique_lock<std::mutex> lock(mtx);//创建一个互斥量封装锁
std::unique_lock<std::timed_mutex> lg(mtx, std::defer_lock); //lock
if (lg.try_lock_for(std::chrono::seconds(2)))
{
std::this_thread::sleep_for(std::chrono::seconds(1));
share_data++;
}
//创建一个唯一锁,但不立即上锁,然后尝试在2秒内获取锁。如果获取成功,则执行if语句块内的代码;
//如果超时(2秒内没获取到锁),则跳过if块(或者进入else分支,如果有的话)。
condition_variable 与其使用场景
条件变量是C++多线程编程中线程同步的核心工具,用于线程间的等待和通知机制。
cpp
#include <string>
#include <condition_variable>
#include <queue>
std::queue<int> g_queue;
std::condition_variable g_cv;
std::mutex mtx;
void Producer()
{
for (int i=0; i<10; i++)
{
{
std::unique_lock<std::mutex> lock(mtx);
g_queue.push(i);
//通知消费者来领取任务
g_cv.notify_one();
std::cout << "Producer :" << i << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void Consumer()
{
while (true)
{
std::unique_lock<std::mutex> lock(mtx);
bool isempty = g_queue.empty();
//如果队列为空,就要等待
g_cv.wait(lock, []() { return !g_queue.empty(); });
int value = g_queue.front();
g_queue.pop();
std::cout << "Consumer :" << value << std::endl;
}
}
int main()
{
std::thread producer_thread(Producer);
std::thread consumer_thread(Consumer);
producer_thread.join();
consumer_thread.join();
return 0;
}
std::function<>
function类似于auto,是C++标准库提供的一个通用可调用对象包装器,可以表示函数,函数对象,lambda表达式,反正有点抽象,但是使用简单:
cpp
# include <iostream>
# include <functional>
typedef std::function<int(int, int)> comfun;
// 普通函数
int add(int a, int b) { return a + b; }
// lambda表达式
auto mod = [](int a, int b){ return a % b; };
// 函数对象类
struct divide{
int operator()(int denominator, int divisor){
return denominator/divisor;
}
};
int main(){
comfun a = add;
comfun b = mod;
comfun c = divide();
std::cout << a(5, 3) << std::endl;
std::cout << b(5, 3) << std::endl;
std::cout << c(5, 3) << std::endl;
}
std::bind()
std::bind 是 C++11 标准库中的一个函数适配器(function adapter),用于部分应用(partial application)和参数绑定(parameter binding)。它创建一个新的可调用对象(callable object),将原函数的某些参数固定为特定值。
cpp
#include <iostream>
#include <functional>
class A {
public:
void fun_3(int k,int m) {
std::cout << "print: k = "<< k << ", m = " << m << std::endl;
}
};
void fun_1(int x,int y,int z) {
std::cout << "print: x = " << x << ", y = " << y << ", z = " << z << std::endl;
}
void fun_2(int &a,int &b) {
++a;
++b;
std::cout << "print: a = " << a << ", b = " << b << std::endl;
}
int main(int argc, char * argv[]) {
//f1的类型为 function<void(int, int, int)>
auto f1 = std::bind(fun_1, 1, 2, 3); //表示绑定函数 fun 的第一,二,三个参数值为: 1 2 3
f1(); //print: x=1,y=2,z=3
auto f2 = std::bind(fun_1, std::placeholders::_1, std::placeholders::_2, 3);
//表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别由调用 f2 的第一,二个参数指定
f2(1, 2); //print: x=1,y=2,z=3
auto f3 = std::bind(fun_1, std::placeholders::_2, std::placeholders::_1, 3);
//表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别由调用 f3 的第二,一个参数指定
//注意: f2 和 f3 的区别。
f3(1, 2); //print: x=2,y=1,z=3
int m = 2;
int n = 3;
auto f4 = std::bind(fun_2, std::placeholders::_1, n); //表示绑定fun_2的第一个参数为n, fun_2的第二个参数由调用f4的第一个参数(_1)指定。
f4(m); //print: a=3,b=4
std::cout << "m = " << m << std::endl; //m=3 说明:bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的,如m
std::cout << "n = " << n << std::endl; //n=3 说明:bind对于预先绑定的函数参数是通过值传递的,如n
A a;
//f5的类型为 function<void(int, int)>
auto f5 = std::bind(&A::fun_3, &a, std::placeholders::_1, std::placeholders::_2); //使用auto关键字
f5(10, 20); //调用a.fun_3(10,20),print: k=10,m=20
std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);
fc(10, 20); //调用a.fun_3(10,20) print: k=10,m=20
return 0;
}
可变参数模板函数
F:一个类型参数,代表第一个函数参数 f 的类型
Args...:可变类型参数包,代表后续所有参数的类型,可以是0个
cpp
template< class F, class... Args >
template< typename F, typename... Args >
#include <iostream>
// 递归终止函数
void print() {
std::cout << std::endl;
}
// 可变参数模板函数
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first << " ";
print(args...); // 递归调用
}
int main() {
// 测试
print(1, 2, 3); // 输出: 1 2 3
print("Hello", "World", 3.14); // 输出: Hello World 3.14
print('A', 42, "test", 2.5); // 输出: A 42 test 2.5
print(); // 输出空行
return 0;
}
std::forward
std::forward是C++11中引入的一个函数模板,用于实现完美转发(Perfect Forwarding)。它的作用是根据传入的参数,决定将参数以左值引用还是右值引用的方式进行转发。
std::forward与完美转发详解
详解enqueue函数
cpp
//加任务
template<typename F, typename... Args>
void enqueue(F&& f, Args&&... args)
{
//函数绑定
std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
{
std::unique_lock<std::mutex> lock(mtx);
tasks.emplace(std::move(task));
}
//通知线程
condition.notify_one();
}
cpp
void enqueue(F&& f, Args&&... args) //实现了通用引用
//三种参数传递方式的对比
// 方式1:值传递(拷贝) - ❌ 最差
template<typename F, typename... Args>
void enqueue_copy(F f, Args... args) {
// 每次调用都会复制 f 和所有 args
std::function<void()> task(std::bind(f, args...));
// ...
}
// 方式2:常量引用 - ❌ 不够好
template<typename F, typename... Args>
void enqueue_const(const F& f, const Args&... args) {
// 不能接受右值,不能移动
std::function<void()> task(std::bind(f, args...));
// ...
}
// 方式3:通用引用(完美转发) - ✅ 最佳
template<typename F, typename... Args>
void enqueue(F&& f, Args&&... args) {
// 完美转发,保持值类别
std::function<void()> task(std::bind(
std::forward<F>(f),
std::forward<Args>(args)...
));
// ...
}
std::function<void()> task(std::bind(std::forward(f), std::forward(args)...));
cpp
bind是将f函数,args参数包进行绑定后返回一个无返回值无参数的函数传给function<void()>
可以直接调用task()来实现功能,相当于调用了函数 f(args...)
eg:
template<typename F, typename... Args>
std::function<void()> make_task(F&& f, Args&&... args) {
return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}
// 使用
auto task = make_task(print_values, 42, 3.14, "hello");
task();
std::move()
std::move 是 C++11 引入的一个强制类型转换工具,用于将左值转换为右值引用,从而启用移动语义。
可以避免不必要的深拷贝,减少内存和时间损耗
cpp
#include <iostream>
#include <string>
#include <utility> // std::move
int main() {
std::string str1 = "Hello, World!";
std::string str2 = "Goodbye";
std::cout << "Before move:" << std::endl;
std::cout << "str1: " << str1 << std::endl; // "Hello, World!"
std::cout << "str2: " << str2 << std::endl; // "Goodbye"
// 使用 std::move 将 str1 的内容移动到 str2
str2 = std::move(str1);
std::cout << "\nAfter move:" << std::endl;
std::cout << "str1: " << str1 << std::endl; // 未定义(通常是空)
std::cout << "str2: " << str2 << std::endl; // "Hello, World!"
return 0;
}