线程池
简单的线程池
在 C++11 中,你可以使用 std::thread 和 std::mutex 等标准库来实现一个简单的线程池。
这个代码定义了一个简单的线程池类 ThreadPool
,可以用于提交任务并在多个线程上执行。
下面是代码的主要部分和功能:
-
ThreadPool
类:- 构造函数接受线程池大小作为参数。
addTask
方法用于向线程池添加任务。~ThreadPool
析构函数在销毁线程池时等待所有任务完成。worker
函数是线程的执行函数,它从任务队列中获取任务并执行。
-
在
main
函数中,创建了一个大小为 4 的线程池。- 然后,通过调用
addTask
方法提交了 10 个任务到线程池。 - 最后,创建了与线程池大小相同的线程,并启动它们来执行
worker
函数。
- 然后,通过调用
这个线程池的实现使用了条件变量 cv
和互斥锁 mtx
来同步任务的添加和线程的唤醒。当任务队列为空时,线程会被阻塞在 cv.wait
函数上,直到有任务可用。
以下是一个简单的示例代码:
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <queue>
#include <functional>
#include <condition_variable>
std::mutex m; // 互斥使用std::cout进行输出
class ThreadPool {
public:
// 线程池大小
size_t poolSize;
// 任务队列
std::queue<std::function<void()>> tasks;
// 锁
std::mutex mtx;
// 条件变量
std::condition_variable cv;
// 线程数
size_t numThreads;
public:
ThreadPool(size_t poolSize) : poolSize(poolSize), numThreads(0) {}
void addTask(const std::function<void()> &task) {
std::unique_lock<std::mutex> lock(mtx);
tasks.push(task);
// 通知一个等待线程
cv.notify_one();
}
~ThreadPool() {
// 等待所有任务n完成
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [=]{ return tasks.empty();});
}
void worker() {
for(;;) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(mtx);
// 等待任务
cv.wait(lock, [=]{ return !tasks.empty();});
task = tasks.front();
tasks.pop();
}
// 执行任务
task();
// 停顿1秒,便于观察线程运行
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
};
int main() {
ThreadPool pool(4);
for (int i = 0; i < 10; i++) {
pool.addTask([=]{
std::lock_guard<std::mutex> lck(m);
std::cout << "Task " << i << " is running on thread " << std::this_thread::get_id() << std::endl;
});
}
pool.addTask([=]{
std::lock_guard<std::mutex> lck(m);
std::cout << "All task are done " << std::this_thread::get_id() << std::endl;
});
std::vector<std::thread> threads;
for (size_t i = 0; i < pool.poolSize; i++) {
threads.push_back(std::thread(&ThreadPool::worker, &pool));
/*
具体来说,std::thread 函数需要一个函数指针作为参数,以及一些可选的参数。在这里,传递了 &pool 作为第二个参数,它是一个指向 ThreadPool 对象的指针。
通过传递 &pool,新创建的线程将能够访问和操作 pool 对象的成员变量和方法。这样,多个线程可以同时执行 ThreadPool::worker 函数,并且通过共享 pool 对象来协作完成任务的分配和执行。
传递地址的方式使得线程之间可以共享对同一个对象的访问,从而实现了线程池的并发执行和任务的分配。
*/
pool.numThreads++;
}
for (auto &thread : threads) {
thread.join();
}
return 0;
}
/*
Task 0 is running on thread 140543688439488
Task 1 is running on thread 140543671654080
Task 2 is running on thread 140543680046784
Task 3 is running on thread 140543543736000
Task 4 is running on thread 140543543736000
Task 6 is running on thread 140543680046784
Task 5 is running on thread 140543688439488
Task 7 is running on thread 140543671654080
Task 8 is running on thread 140543688439488
Task 9 is running on thread 140543671654080
All task are done 140543543736000
*/
在上面的示例中, ThreadPool 类有以下几个关键部分:
- poolSize 表示线程池的大小。
- tasks 是一个任务队列,使用 std::queue 实现。
- mtx 是一个互斥锁,用于保护任务队列的访问。
- cv 是一个条件变量,用于通知等待的线程有任务可用。
- numThreads 表示当前运行的线程数。
在 addTask 方法中,使用 std::unique_lock 来获取互斥锁,并将任务添加到任务队列中。然后,使用 cv.notify_one 通知一个等待的线程。
在 worker 方法中,使用循环不断等待任务。在循环中,使用 std::unique_lock 来获取互斥锁,并使用 cv.wait 等待条件变量的信号。当有任务可用时,从队列中取出任务并执行。
在 main 函数中,创建一个线程池,并向其添加多个任务。最后,启动所有的线程,并等待所有任务完成。
请注意,这只是一个简单的线程池实现示例,可能不适用于所有情况。在实际应用中,你可能需要根据具体需求进行调整和优化。
C++ 线程池 (学习)
多用组合、少用继承
基于接口而非实现进行编程