前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >条件变量使用细节

条件变量使用细节

作者头像
opencode
发布2022-12-26 15:32:03
6710
发布2022-12-26 15:32:03
举报
文章被收录于专栏:知识同步

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • CRITICAL_SECTION的使用
  • 条件变量的虚拟唤醒
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档