c 服务器开发精髓有感
消费者和生产者模式,创建5个消费者,一个生产者,生产者每隔一秒生产一个任务,通知所有消费者去处理
代码语言:javascript复制#include <Windows.h>
#include <iostream>
#include <list>
class Task
{
public:
Task(int taskID)
{
this->taskID = taskID;
}
void doTask()
{
std::cout << "handle a task, taskID: " << taskID << ", threadID: " << GetCurrentThreadId() << std::endl;
}
private:
int taskID;
};
CRITICAL_SECTION myCriticalSection;
CONDITION_VARIABLE myConditionVar;
std::list<Task*> tasks;
DWORD WINAPI consumerThread(LPVOID param)
{
Task* pTask = NULL;
while (true)
{
//进入临界区
EnterCriticalSection(&myCriticalSection);
while (tasks.empty())
{
//如果SleepConditionVariableCS挂起,挂起前会离开临界区,不往下执行。
//当发生变化后,条件合适,SleepConditionVariableCS将直接进入临界区。
SleepConditionVariableCS(&myConditionVar, &myCriticalSection, INFINITE);
}
pTask = tasks.front();
tasks.pop_front();
//SleepConditionVariableCS被唤醒后进入临界区,
//为了让其他线程有机会操作tasks,这里需要再次离开临界区
LeaveCriticalSection(&myCriticalSection);
if (pTask == NULL)
continue;
pTask->doTask();
delete pTask;
pTask = NULL;
}
return 0;
}
DWORD WINAPI producerThread(LPVOID param)
{
int taskID = 0;
Task* pTask = NULL;
while (true)
{
pTask = new Task(taskID);
//进入临界区
EnterCriticalSection(&myCriticalSection);
tasks.push_back(pTask);
std::cout << "produce a task, taskID: " << taskID << ", threadID: " << GetCurrentThreadId() << std::endl;
LeaveCriticalSection(&myCriticalSection);
WakeConditionVariable(&myConditionVar);
taskID ;
//休眠1秒
Sleep(1000);
}
return 0;
}
int main()
{
InitializeCriticalSection(&myCriticalSection);
InitializeConditionVariable(&myConditionVar);
//创建5个消费者线程
HANDLE consumerThreadHandles[5];
for (int i = 0; i < 5; i)
{
consumerThreadHandles[i] = CreateThread(NULL, 0, consumerThread, NULL, 0, NULL);
}
//创建一个生产者线程
HANDLE producerThreadHandle = CreateThread(NULL, 0, producerThread, NULL, 0, NULL);
//等待生产者线程退出
WaitForSingleObject(producerThreadHandle, INFINITE);
//等待消费者线程退出
for (int i = 0; i < 5; i)
{
WaitForSingleObject(consumerThreadHandles[i], INFINITE);
}
DeleteCriticalSection(&myCriticalSection);
return 0;
}
CRITICAL_SECTION的使用
这里使用CRITICAL_SECTION而不是Mutex的原因是CRITICAL_SECTION不是内核级的互斥体,更快一些,两者有如下区别:
CRITICAL_SECTION | Mutex | |
---|---|---|
性能和速度 | 快。Critical Section本身不是内核对象,相关函数(EnterCriticalSection,eaveCriticalSection)只有当想要获得的锁正好被别的线程拥有时才会退化成和Mutex一样,即转换到内核模式,发费600个左右的 CPU指令周期。 | 慢。Mutex 是内核对象,相关函数的执行 (WaitForSingleObject,ReleaseMutex)需要用户模式(User Mode)到内核模式(Kernel Mode)的转换 |
能否跨越进程(Process)边界 | 否 | 能 |
进入临界区/加锁 | EnterCriticalSection | lock |
离开临界区/释放 | LeaveCriticalSection | unlock |
条件变量的虚拟唤醒
代码语言:javascript复制while (tasks.empty())
{
//如果SleepConditionVariableCS挂起,挂起前会离开临界区,不往下执行。
//当发生变化后,条件合适,SleepConditionVariableCS将直接进入临界区。
SleepConditionVariableCS(&myConditionVar, &myCriticalSection, INFINITE);
}
这里使用while而不是if的原因有两个,一个数所谓的虚拟唤醒,一个是逻辑上就需要
对于虚拟唤醒这个问题,其实是一个内核底层的问题,出现虚拟唤醒的原因是底层为了不错过信号而产生的,这里就不深入探讨,也不需要,这不是用户层能改变的。
第二个,如果不使用while,那如果判断完了不就往下走了,这不符合。
https://blog.csdn.net/llmblcwwmm/article/details/106820773