最近任务需要在MFC下做多线程生产者消费者模式的东西,我找了半天貌似MFC没有类似Java里面BlockingQueue那样的工具(也许是我手残没找到)。

网上好像也有很多大佬去实现这个。但是我没仔细去找,看了看一些资料就想着造个轮子玩玩。

实现如下:

主要是利用CCriticalSection保护内置的std::list,然后用CEvent来实现生产者消费者的同步。

参考资料:http://stackoverflow.com/questions/6683356/c-templated-producer-consumer-blockingqueue-unbounded-buffer-how-do-i-end-el

接口文件:IBlockingQueue.h

 #pragma once

 template <class T>
class IBlockingQueue
{
public:
virtual ~IBlockingQueue() {} // 为了让实现这个接口的类的析构函数能被正确调用,参考:http://blog.csdn.net/chaoguodong/article/details/6935524
virtual int size() = ;
virtual T pop_front() = ;
virtual T pop_back() = ;
virtual void push_front(T val) = ;
virtual void push_back(T val) = ;
virtual bool empty() = ;
virtual void stop() = ;
virtual bool is_stop() = ;
};

实现文件:BlockingQueue.h

 #pragma once

 #include <afxmt.h>
#include <list> #include "Bridge.h" #include "StoppingException.h" // 参考资料:http://stackoverflow.com/questions/6683356/c-templated-producer-consumer-blockingqueue-unbounded-buffer-how-do-i-end-el template <class T>
class CBlockingQueue : public IBlockingQueue<T>
{
CCriticalSection m_cs; // 保护 m_lst
CEvent m_emptyEvent; // m_lst 为空 就 reset,m_lst 不为空 就 set 放行
std::list<T> m_lst;
bool m_bStop;
public:
CBlockingQueue();
CBlockingQueue(const CBlockingQueue<T>& obj);
CBlockingQueue<T>& operator=(const CBlockingQueue<T>& obj);
virtual int size();
virtual T pop_front();
virtual T pop_back();
virtual void push_front(T val);
virtual void push_back(T val);
virtual bool empty();
virtual void stop();
virtual bool is_stop();
}; template <class T>
CBlockingQueue<T>::CBlockingQueue()
: m_emptyEvent(FALSE, TRUE, NULL, NULL), m_bStop(false) // 初始为RESET,不自动RESET
{} template <class T>
CBlockingQueue<T>::CBlockingQueue(const CBlockingQueue<T>& obj)
: m_emptyEvent(FALSE, TRUE, NULL, NULL), m_bStop(false) // 初始为RESET,不自动RESET
{
m_cs.Lock();
obj.m_cs.Lock();
m_lst = obj.m_lst;
obj.m_cs.Unlock();
m_cs.Unlock();
} template <class T>
CBlockingQueue<T>& CBlockingQueue<T>::operator = (const CBlockingQueue<T>& obj)
{
m_cs.Lock();
obj.m_cs.Lock();
m_lst = obj.m_lst;
obj.m_cs.Unlock();
m_cs.Unlock();
return *this;
} template <class T>
int CBlockingQueue<T>::size()
{
m_cs.Lock();
int sz = ;
sz = m_lst.size();
m_cs.Unlock();
return sz;
}
template <class T>
T CBlockingQueue<T>::pop_front()
{
T val;
bool done = false; // val 是否从 m_lst 中取出
while (!done)
{
m_cs.Lock();
if (!m_bStop) // 如果另一个线程在m_lst非空的时候调用了stop,然后那个线程结束了。那么调用pop_front的线程就无限等待了。
{
m_cs.Unlock();
::WaitForSingleObject(m_emptyEvent.m_hObject, INFINITE);
}
else
m_cs.Unlock();
m_cs.Lock();
if (m_lst.empty())
{
if (m_bStop) // 先检测 empty 再检测 stop,因为 stop 为 true 的时候 m_lst 中仍然可能有数据没取出。要知道 stop 是为了提示其他线程没有更多的数据了,而不是为了强行中断其他线程获取数据。
{
m_cs.Unlock();
throw StoppingException();
}
}
else
{
val = m_lst.front();
m_lst.pop_front();
if (m_lst.empty())
m_emptyEvent.ResetEvent();
done = true;
}
m_cs.Unlock();
}
return val;
}
template <class T>
T CBlockingQueue<T>::pop_back()
{
T val;
bool done = false; // val 是否从 m_lst 中取出
while (!done)
{
m_cs.Lock();
if (!m_bStop)
{
m_cs.Unlock();
::WaitForSingleObject(m_emptyEvent.m_hObject, INFINITE);
}
else
m_cs.Unlock();
m_cs.Lock();
if (m_lst.empty())
{
if (m_bStop)
{
m_cs.Unlock();
throw StoppingException();
}
}
else
{
val = m_lst.back();
m_lst.pop_back();
if (m_lst.empty())
m_emptyEvent.ResetEvent();
done = true;
}
m_cs.Unlock(); }
return val;
}
template <class T>
void CBlockingQueue<T>::push_front(T val)
{
m_cs.Lock();
m_lst.push_front(val);
m_emptyEvent.SetEvent();
m_cs.Unlock();
}
template <class T>
void CBlockingQueue<T>::push_back(T val)
{
m_cs.Lock();
m_lst.push_back(val);
m_emptyEvent.SetEvent();
m_cs.Unlock();
}
template <class T>
bool CBlockingQueue<T>::empty()
{
m_cs.Lock();
bool bEmpty = m_lst.empty();
m_cs.Unlock();
return bEmpty;
}
template <class T>
bool CBlockingQueue<T>::is_stop()
{
m_cs.Lock();
bool bStop = m_bStop;
m_cs.Unlock();
return bStop;
}
template <class T>
void CBlockingQueue<T>::stop()
{
m_cs.Lock();
m_bStop = true;
m_emptyEvent.SetEvent();
m_cs.Unlock();
}

实现文件:BlockingQueue.cpp

#include "BlockingQueue.h"

StoppingException::StoppingException() {}

StoppingException::~StoppingException() {}

测试文件:MyApp.h

#pragma once

#include <afxwin.h>

class CMyApp :
public CWinApp
{
public:
virtual BOOL InitInstance();
};

测试文件:MyApp.cpp

#include "MyApp.h"

#include "BlockingQueue.h"

using namespace std;

class CMainWindow :
public CFrameWnd
{
public:
CMainWindow();
DECLARE_MESSAGE_MAP()
afx_msg void OnClose();
}; CMainWindow::CMainWindow()
{
Create(NULL, _T("The Hello Application"), WS_OVERLAPPED | WS_CAPTION |
WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME,
CRect(, , , ));
} CMyApp myApp; // 共享的数据'
IBlockingQueue<int>* pBQ = new CBlockingQueue<int>(); #define NUM_PRODUCER 9 // 生产者个数
#define NUM_CONSUMER 5 // 消费者个数 CWinThread* pThreadProducer[NUM_PRODUCER]; // 生产者线程
CWinThread* pThreadConsumer[NUM_CONSUMER]; // 消费者线程
HANDLE hConsumer[NUM_CONSUMER]; // 消费者HANDLE // 生产
UINT Produce(LPVOID pParam)
{
for (int i = ; i < ; ++i)
{
TRACE(_T("Producer[%d]Producing: %d\n"), ::GetCurrentThreadId(), i);
pBQ->push_back(i);
TRACE(_T("Producer[%d]Producing: %d\n"), ::GetCurrentThreadId(), i);
pBQ->push_front(i);
TRACE(_T("Producer[%d]Sleeping...\n"), ::GetCurrentThreadId());
::Sleep();
}
TRACE(_T("Producer[%d]Exiting...\n"), ::GetCurrentThreadId());
return ;
}
// 消费
UINT Consume(LPVOID pParam)
{
try {
while (true)
{
TRACE(_T("Consumer[%d]Waiting...\n"), ::GetCurrentThreadId());
int val = pBQ->pop_front();
TRACE(_T("Consumer[%d]Consuming: %d\n"), ::GetCurrentThreadId(), val);
val = pBQ->pop_back();
TRACE(_T("Consumer[%d]Consuming: %d\n"), ::GetCurrentThreadId(), val);
}
}
catch (StoppingException& e)
{
TRACE(_T("Consumer[%d]%s...\n"), ::GetCurrentThreadId(), e.msg);
}
TRACE(_T("Consumer[%d]Exiting...\n"), ::GetCurrentThreadId());
return ;
}
// 主线程(UI)
BOOL CMyApp::InitInstance()
{ _CrtSetBreakAlloc(); m_pMainWnd = new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow(); // 共享的初始数据
for (int i = ; i < ; ++i)
{
pBQ->push_back(i);
}
// 创建消费者线程
for (int i = ; i < NUM_CONSUMER; ++i)
{
CWinThread* pThread = ::AfxBeginThread(Consume, NULL, THREAD_PRIORITY_NORMAL, , CREATE_SUSPENDED);
pThread->m_bAutoDelete = FALSE;
pThreadConsumer[i] = pThread;
hConsumer[i] = pThread->m_hThread;
}
// 启动消费者线程
for (int i = ; i < NUM_CONSUMER; ++i)
{
pThreadConsumer[i]->ResumeThread();
}
// 创建生产者线程
for (int i = ; i < NUM_PRODUCER; ++i)
{
CWinThread* pThread = ::AfxBeginThread(Produce, NULL, THREAD_PRIORITY_NORMAL, , CREATE_SUSPENDED);
pThread->m_bAutoDelete = FALSE;
pThreadProducer[i] = pThread;
}
// 启动生产者线程
for (int i = ; i < NUM_PRODUCER; ++i)
{
pThreadProducer[i]->ResumeThread();
} return TRUE;
} BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
ON_WM_CLOSE()
END_MESSAGE_MAP() // 退出主线程
void CMainWindow::OnClose()
{
pBQ->stop(); ::WaitForMultipleObjects(NUM_CONSUMER, hConsumer, TRUE, INFINITE); for (int i = ; i < NUM_CONSUMER; ++i)
{
delete pThreadConsumer[i];
} for (int i = ; i < NUM_PRODUCER; ++i)
{
delete pThreadProducer[i];
} delete pBQ; CFrameWnd::OnClose();
}

【造轮子】MFC实现BlockingQueue的更多相关文章

  1. 避免重复造轮子的UI自动化测试框架开发

    一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...

  2. 【疯狂造轮子-iOS】JSON转Model系列之二

    [疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...

  3. 【疯狂造轮子-iOS】JSON转Model系列之一

    [疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...

  4. h5engine造轮子

    基于学习的造轮子,这是一个最简单,最基础的一个canvas渲染引擎,通过这个引擎架构,可以很快的学习canvas渲染模式! 地址:https://github.com/RichLiu1023/h5en ...

  5. 我为什么还要造轮子?欠踹?Monk.UI表单美化插件诞生记!

    背景 目前市场上有很多表单美化的UI,做的都挺不错,但是他们都有一个共同点,那就是90%以上都是前端工程师开发的,导致我们引入这些UI的时候,很难和程序绑定.所以作为程序员的我,下了一个决定!我要自己 ...

  6. 「iOS造轮子」之UIButton 用Block响应事件

    俗语说 一个不懒的程序员不是好程序员 造轮子,也只是为了以后更好的coding. coding,简易明了的代码更是所有程序员都希望看到的 无论是看自己的代码,还是接手别人的代码 都希望一看都知道这代码 ...

  7. 重复造轮子感悟 – XLinq性能提升心得

    曾经的两座大山 1.EF 刚接触linq那段时间,感觉这家伙好神奇,语法好优美,好厉害.后来经历了EF一些不如意的地方,就想去弥补,既然想弥补,就必须去了解原理.最开始甚至很长一段时间都搞不懂IQue ...

  8. GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。

    1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...

  9. 第27篇 重复造轮子---模拟IIS服务器

    在写程序的时候,重复造轮子是程序员的一个大忌,很多人对重复造轮子持有反对的态度,但是我觉得这个造轮子的过程,是对于现有的知识的一个深入的探索的过程,虽然我们不可能把轮子造的那么的完善,对于现在有的东西 ...

  10. 用Go造轮子-管理集群中的配置文件

    写在前面 最近一年来,我都在做公司的RTB广告系统,包括SSP曝光服务,ADX服务和DSP系统.因为是第一次在公司用Go语言实现这么一个大的系统,中间因为各种原因造了很多轮子.现在稍微有点时间,觉着有 ...

随机推荐

  1. [think]关于个人发展值得记住的一些建议 听别人的话,即使你不想听 不要只做不想 成功不能被复制,但失败总在不停复制。看看别人是怎么倒下的,你可以更早地成功

    [think]关于个人发展值得记住的一些建议 偶然看到一篇采访周爱民的文章,里面的一些建议虽然朴实无华,却感觉很有道理,特此记录: 记者:对于程序员的技术发展和职业规划能否给大家一些建议呢?----- ...

  2. vim自动缩进设置

    需要软件 vim 下载地址 http://www.vim.org   code_complete.vim 插件 http://www.vim.org/scripts/script.php?script ...

  3. Android adjustresize全屏无效问题

    屏模式下,即使将activity的windowSoftInputMode的属性设置为:adjustResize,在键盘显示时它未将Activity的Screen向上推动,所以你Activity的vie ...

  4. Eclipse自动部署项目到Tomcat的webapps下的有效方法

    开发JavaEE项目,常用的工具有MyEclipse,Eclipse,netBeans等,我比较喜欢用Eclipse,因为相比MyEclipse体积小很多,响应速度也快,且足以满足需求,我喜欢简洁的编 ...

  5. ViewPager 源码分析(一) —— setAdapter() 与 populate()

    写在前面 做安卓也有一定时间了,虽然常用控件都已大致掌握,然而随着 Android N 的发布,不自觉的愈发焦虑起来.说来惭愧,Android L 的 Material Design 库里的许多控件都 ...

  6. [javase学习笔记]-7.2 构造函数与一般函数的差别

    这一节我们简单学习一下构造函数与一般函数之间的差别所在. 那么它们有什么差别呢,结合上一节,我们能够总结出下面两点差别: 第一个差别: 构造函数:对象创建时,就会调用与之相应的构造函数,对对象进行初始 ...

  7. LIGHT OJ 1199 - Partitioning Game

    传送门 1199 - Partitioning Game    PDF (English) problem=1199" style="color:rgb(79,107,114)&q ...

  8. 近期对招聘Android开发者的一些思考

    公司要招聘Android开发者,故面试了大概十来个人.由于是小公司,所以来的人大多是90后,比較年轻.90后大概二十三四岁吧,从简历上看都写了一到两年的工作经验. 也由于是小公司,所以对工作经验这些没 ...

  9. DataGridView控件使用大全说明-各种常用操作与高级操作

    DataGridView控件 DataGridView是用于Windows Froms 2.0的新网格控件.它可以取代先前版本中DataGrid控件,它易于使用并高度可定制,支持很多我们的用户需要的特 ...

  10. 安装ganglia过程中出现错误 perl(RRDp) is needed by rrdtool-1.2.30-1.el5.rf.x86_64

    用rpm -ivh *.rpm安装ganglia的rpm包,然后出现下面的错误: warning: rrdtool-1.2.30-1.el5.rf.x86_64.rpm: Header V3 DSA/ ...