设计线程池
大纲
成员变量
成员函数
先完成一个基本框架的设计
构造
由于我们调用是说我们自己设计的thread
start wait stop
和单线程那里类似
主要是让所有线程都开启上面那些函数
测试一下
以上基本功能已经实现,接下来我们对其进行改变一下
这个任务相当于是全局的,要是我们想要让他在类里面实现该又怎么做呢??
我们将任务函数变成成员函数
编译的时候发现失败了这是为什么????
普通成员函数隐含参数this,类型是void (ThreadPool<int>::*)(),不是普通函数指针void (*)(),Thread 类构造只接收普通函数指针,编译直接报错。
那么应该如何解决???
首先我们可以先让他变成static
当然我们也可以用
直接用lambda 捕获 this传入,完美解决!
std::function<void()>可以接收:
- 普通全局函数
- lambda 表达式(可以捕获
this) - bind 绑定成员函数
上面谈完这个问题,
接下来我们对任务进行更加细致的修改
再执行任务的时候是要进行加锁和减锁的
因为要求每个线程调用的任务是不重复的
再临界区内我们哟啊检测任务,
如果有任务该怎么办
没有任务有该怎么办
有任务,取任务就是将公共资源变成私有的
enqueue
唤醒线程的逻辑是只要有有线程处于休眠状态就进行唤醒
测试
对于stop逻辑的更新
需求:
- 使用
_isrunning标志位控制线程池运行状态 - 队列中剩余任务全部执行完毕之后,工作线程才允许退出
- 使用
Broadcast唤醒所有休眠的工作线程 - 线程处理函数(handler)内部判断条件,自动 break 退出循环
也就是说对于handler函数那里还需要做出调整
当任务不为空的时候,要继续执行,也就是说不能让线程休眠
当线程池要退出时,线程也不能休眠,
任意满足上面两条都是不可以进行休眠
--->那什么时候可以进行休眠呢??
当上面两条都不满足的时候
测试
class Task { public: ~Task() { } Task(int x, int y) : _x(x), _y(y) { } void Excute() { _ret = _x + _y; } void operator()() { _ret = _x + _y; } string PrintAns() { return to_string(_x) + "+" + to_string(_y) + "=" + to_string(_ret); } string PrintQuestion() { return to_string(_x) + "+" + to_string(_y) + "=?"; } private: int _x; int _y; int _ret; }; using task_t = function<void()>; int main() { srand(time(nullptr) ^ getpid()); ENABLE_CONSOLESTRATEGY(); unique_ptr<ThreadPool<Task>> pool = make_unique<ThreadPool<Task>>(); pool->Start(); int cnt = 10; while (cnt--) { int x = rand() % 10 + 1; int y = rand() % 20 + 1; Task t(x, y); sleep(1); pool->Enqueue(t); sleep(1); } pool->Wait(); return 0; }void handlertask() { char buf[64]; pthread_getname_np(pthread_self(), buf, sizeof(buf)); while (1) { // 保护临界区 _mutex.Lock(); // 检测任务. 不休眠->1.队列不为空2.线程池退出 while (_qu_task.empty()&&_isrunning) { // 没有任务 _slaver_sleep_count++; _cond.Wait(_mutex); _slaver_sleep_count--; } // 有任务或者线程池退出 // 1&&2 ---->break if(!_isrunning&&_qu_task.empty()) { _mutex.Unlock(); break; } // 如果模板是一个类要怎么做 T mytask = _qu_task.front(); _qu_task.pop(); // 处理任务??需要在临界区内执行吗??? // 不需要不然就成为单线程 _mutex.Unlock(); LOG(LogLevel::INFO) << "my name:" << buf; mytask(); LOG(LogLevel::INFO) << "my job:" <<mytask.PrintAns(); } }