惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
P
Privacy International News Feed
P
Proofpoint News Feed
Security Archives - TechRepublic
Security Archives - TechRepublic
C
CXSECURITY Database RSS Feed - CXSecurity.com
Know Your Adversary
Know Your Adversary
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Attack and Defense Labs
Attack and Defense Labs
NISL@THU
NISL@THU
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
GbyAI
GbyAI
N
News and Events Feed by Topic
N
News | PayPal Newsroom
Y
Y Combinator Blog
C
CERT Recently Published Vulnerability Notes
N
Netflix TechBlog - Medium
S
Security Affairs
Spread Privacy
Spread Privacy
罗磊的独立博客
腾讯CDC
MyScale Blog
MyScale Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
LINUX DO - 热门话题
The Cloudflare Blog
L
LangChain Blog
博客园_首页
H
Hacker News: Front Page
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
SecWiki News
SecWiki News
A
Arctic Wolf
爱范儿
爱范儿
Google Online Security Blog
Google Online Security Blog
T
Threat Research - Cisco Blogs
Hacker News - Newest:
Hacker News - Newest: "LLM"
有赞技术团队
有赞技术团队
The GitHub Blog
The GitHub Blog
Cyberwarzone
Cyberwarzone
博客园 - 叶小钗
V
Visual Studio Blog
V
V2EX
T
Tailwind CSS Blog
Project Zero
Project Zero
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
MongoDB | Blog
MongoDB | Blog
D
Docker

博客园 - hcfalan

Mybatis分页功能 安聊服务端Netty的应用 安聊系统1.0发布 VC中发送电子邮件 自绘窗口边框和标题栏 Javascript 时间日期转换 Java Threading - Consumer&Producer Boost socket 同步编程示例(服务端,客户端) MFC CSocket的跨线程问题 用于主题检测的临时日志(54b8134e-5e4a-46fc-95af-c75ccf06d66e - 3bfe001a-32de-4114-a6b4-4005b770f6d7) 网络电话 VC 剪贴板操作 The 1st Boost Serial Demo - hcfalan MS Proxy FTP Sample 《斯坦福大学开放课程: 编程方法学》 VC2005下编译log4cpp的修正方法 - hcfalan - 博客园 适合孩子的书籍 一个最经典的线程类(转) 应用MFC开发高级应用程序(转)
Windows下生产者-消费者问题的解法
hcfalan · 2011-11-30 · via 博客园 - hcfalan

先来描述一下待解决的问题:

有一个仓库,它最多有七个槽位,最开始每个槽位都是空的。当有空槽位的时候,允许生产者往里面放东西。当槽位上有东西时,允许消费者从里面拿东西;满了则不允许再放,空了则不允许再拿。因为仓库设计问题,同一时间内,只允许一个人进去放东西或者拿东西。需要尽最大的效率安排生产者和消费者的工作。可以看到,这里生产和消费的动作不需要做严格的同步,只要规则允许,可以连续生成三次,也可以连续消费三次。而没有说一定要生产一次,消费一次,再生产一次,再消费一次的顺序来。

// productor_consumer.cpp
// Windows 平台对生产者-消费者问题的解决方案

#include <windows.h>
#include <tchar.h>
#include <list>

// 定义仓库的最大槽位
#define WH_SLOT    7

// 定义等待信号灯的超时时间
#define TIMEOUT    3000

// 定义仓库类
class Warehource
{
public:
    Warehource()
    {
        // 创建一个访问仓库的互斥量,生产者和消费者不能同时访问仓库
        m_hAccess = CreateEvent(NULL, FALSE, TRUE, NULL);
    }
    ~Warehource()
    {
        // 关闭访问互斥量
        CloseHandle(m_hAccess);
    }

    // 放入到仓库
    BOOL put(char ch)
    {
        // 尝试获取互斥量
        if (WAIT_OBJECT_0 == WaitForSingleObject(m_hAccess, 100))
        {
            m_slot.push_back(ch);
            printf("Product an item, now warehourse size is %d\n", m_slot.size());
            SetEvent(m_hAccess); //let's next access available
            return TRUE;
        }
        return FALSE;
    }

    // 从仓库取出
    BOOL get(char* ch)
    {
        // 尝试获取互斥量
        if (WAIT_OBJECT_0 == WaitForSingleObject(m_hAccess, 100))
        {
            *ch = m_slot.front();
            m_slot.pop_front();
            printf("Consume an item, now warehourse size is %d\n", m_slot.size());
            SetEvent(m_hAccess); //let's next access available
            return TRUE;
        }
        return FALSE;
    }
private:
    std::list<char> m_slot;
    HANDLE m_hAccess; //对仓库访问要互斥
};

DWORD WINAPI ProductorRun(LPVOID ud);
DWORD WINAPI ConsumerRun(LPVOID ud);

HANDLE g_hProdExit;            //生产者退出的信号
HANDLE g_hFreeSemaphore;    //仓库空闲槽的信号灯,最大值为WH_SLOT

HANDLE g_hConsExit;            //消费者退出的信号
HANDLE g_hDataSemaphore;    //仓库可用槽的信号灯,最大值为WH_SLOT

int _tmain(int argc, _TCHAR* argv[])
{
    Warehource wh;
    g_hProdExit = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_hConsExit = CreateEvent(NULL, FALSE, FALSE, NULL);
    //仓库空闲槽的信号灯,最大值为WH_SLOT,初始值为WH_SLOT,因为仓库
    //一开始都是空的
    g_hFreeSemaphore = CreateSemaphore(NULL, WH_SLOT, WH_SLOT, NULL);
    //仓库可用槽的信号灯,最大值为WH_SLOT,初始值为0,因为仓库
    //一开始都是空的
    g_hDataSemaphore = CreateSemaphore(NULL, 0, WH_SLOT, NULL);

    // 创建生产者和消费者
    HANDLE hProductor = CreateThread(NULL, NULL, ProductorRun, &wh, 0, 0);
    HANDLE hConsumer  = CreateThread(NULL, NULL, ConsumerRun, &wh, 0, 0);

    // let main thread sleep 30 seconds
    Sleep(3000);

    // 结束生产者
    SetEvent(g_hProdExit);
    WaitForSingleObject(hProductor, INFINITE);
    CloseHandle(g_hProdExit);
    CloseHandle(hProductor);

    // 结束消费者
    SetEvent(g_hConsExit);   
    WaitForSingleObject(hConsumer, INFINITE);
    CloseHandle(g_hConsExit);
    CloseHandle(hConsumer);
    // 关闭信号灯
    CloseHandle(g_hFreeSemaphore);
    CloseHandle(g_hDataSemaphore);
    return 0;
}

DWORD WINAPI ProductorRun(LPVOID ud)
{
    BOOL fDone = FALSE;
    HANDLE h2[] = { g_hProdExit, g_hFreeSemaphore };
    DWORD dwWait = 0;

    Warehource* wh = (Warehource*)ud;
    while (!fDone)
    {
        // 等待退出信号或空闲信号
        dwWait = WaitForMultipleObjects(2, h2, FALSE, TIMEOUT);
        if (dwWait == WAIT_OBJECT_0)
        {
            //退出信号
            fDone = TRUE;
        }
        else if (dwWait == WAIT_OBJECT_0+1)
        {
            // 空闲信号,表示仓库中有空闲槽,尝试放入到仓库
            if (wh->put('a'))
            {                
                // 已经成功放入一个到仓库,则置上一个可用槽的信号灯
                ReleaseSemaphore(g_hDataSemaphore, 1, NULL);
            }
            else //访问仓库超时
            {
                // 将熄灭的空闲槽信号灯置回去
                ReleaseSemaphore(g_hFreeSemaphore, 1, NULL);
            }
        }
    }

    return 0;
}

DWORD WINAPI ConsumerRun(LPVOID ud)
{
    BOOL fDone = FALSE;
    HANDLE h2[] = { g_hConsExit, g_hDataSemaphore };
    DWORD dwWait = 0;

    Warehource* wh = (Warehource*)ud;
    while (!fDone)
    {
        // 等待退出信号或可用信号
        dwWait = WaitForMultipleObjects(2, h2, FALSE, TIMEOUT);
        if (dwWait == WAIT_OBJECT_0)
        {
            //退出信号
            fDone = TRUE;
        }
        else if (dwWait == WAIT_OBJECT_0+1)
        {
            // 可用信号,表示仓库中某个槽已有数据,尝试从中取用
            char ch;
            if (wh->get(&ch))
            {                
                // 已经成功从仓库取出一个,则置上一个空闲槽的信号灯
                ReleaseSemaphore(g_hFreeSemaphore, 1, NULL);
            }
            else //访问仓库超时
            {
                // 将熄灭的可用槽信号灯置回去
                ReleaseSemaphore(g_hDataSemaphore, 1, NULL);
            }
        }
    }
    return 0;
}

注意semaphore是可以让多个线程同时访问共享资源的唯一信号类型。