代码以川建国同志的日常为例。川建国同志日常两件事,吹牛(brag)和咆哮(bark)。两件事随机发生,用创建线程的方式实现。
前两段内容部分摘自(https://blog.csdn.net/qq_33723441/article/details/54171230),有改正。
MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,区别在于用户界面线程能处理消息响应,而工作者线程不能。
本文只谈工作者线程的AfxBeginThread ,原型如下:
CWinThread* AFXAPI AfxBeginThread
(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority,
UINT nStackSize,
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs
)
其中:
参数1 线程的入口函数,必须是静态成员函数,声明一定要如下:
static UINT MyThreadFunction( LPVOID pParam );
参数2 传递入线程的参数,类型为LPVOID,所以我们可以传递一个结构体入线程。
参数3~5是默认参数,可以省略:
参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
参数5表示线程的安全属性,NT下有用。
实际中我们经常这样用:
AfxBeginThread(ThreadProc,this);
看到这儿我是挺懵的,参数2应该是给ThreadProc的参数么,怎么给传了个this指针?
后来明白了,通常这里的入口函数ThreadProc是这么个函数:
首先,它是当前类的静态成员函数;
其次,它的参数是当前类;
最后,他的作用是调用当前类的另一个成员函数。
这么说很抽象,因此我想了个例子。
类名是TrumpDaily(川建国同志的日常),.h声明如下:
class CTrumpDaily
{
public:
CString ms_sentence; //汇报川建国言行的CString字符串
void Decide();
void Brag();
void Bark();
static UINT doBrag(void* pParam);
static UINT doBark(void* pParam);
};
.cpp实现:
成员函数是:
TrumpDaily::Decide,川建国决定一下今天做什么,吹牛(brag)还是咆哮(bark)
void CTrumpDaily::Decide()
{
// 创建范围[0,1]的随机数i
int i = rand() % 2 ;
switch (i)
{
case 0:
AfxBeginThread(doBrag, this); //i是0就创建个线程开始吹牛
break;
case 1:
AfxBeginThread(doBark, this); //i是1就创建个线程开始在那吠
break;
}
}
两个线程的入口函数是doBrag和doBark:
UINT CTrumpDaily::doBrag(void* pParam)
{
// 新建一个CTrumpDaily对象,调用其Brag()成员函数
CTrumpDaily* aDay = (CTrumpDaily*)pParam;
aDay->Brag();
return 0;
}
UINT CTrumpDaily::doBark(void* pParam)
{
// 新建一个CTrumpDaily对象,调用其Bark()成员函数
CTrumpDaily* aDay = (CTrumpDaily*)pParam;
aDay->Bark();
return 0;
}
可以看到,这两个入口函数本身不干实事,它俩只是调用干实事的成员函数:
void CTrumpDaily::Brag()
{
ms_sentence = "Trump brags: Nobody lies better than I do.";
}
void CTrumpDaily::Bark()
{
ms_sentence = "Trump barks: CHINA! CHINA!";
}
最后MFC的单击消息按钮处理:
void CPlayMFCDBDlg::OnBnClickedAddButton()
{
// 按钮对应的变量是m_edit_sum,把川普的言行赋给它显示在对话框里
CTrumpDaily aDay; //新建一个CTrumpDaily对象
aDay.Decide(); //让它决定下做哪件事
m_edit_sum = aDay.ms_sentence; //把它的言论传给文本框变量
UpdateData(FALSE); //更新文本框
}
这样一个简单的MFC程序就搭好了,简洁又大方,干净又卫生:
但是这篇博客到此就该完了吗?并没有,以上代码是有bug的。反复戳"Trump Today"按钮,会发现文本框有时有川普的言论,但还有时是空的!多点击此后,程序还会崩溃!为什么?
正确的消息处理函数如下:
void CPlayMFCDBDlg::OnBnClickedAddButton()
{
// 按钮对应的变量是m_edit_sum,把川普的言行赋给它显示在对话框里
CTrumpDaily aDay;
aDay.Decide();
Sleep(100); //这很重要
m_edit_sum = aDay.ms_sentence; //这是雷点
UpdateData(FALSE);
}
Sleep(100)这句为啥重要呢?线程是单独运行的,有肯能主程序已经执行到m_edit_sum = aDay.ms_sentence了,线程刚好也在访问aDay.ms_sentence,这就打架了。至于什么时候打架就是看天意了。
所以加一句Sleep,让主程序等一等它的人民,一切就安好了。