MFC 用AfxBeginThread创建线程

代码以川建国同志的日常为例。川建国同志日常两件事,吹牛(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程序就搭好了,简洁又大方,干净又卫生:

MFC 用AfxBeginThread创建线程_第1张图片

但是这篇博客到此就该完了吗?并没有,以上代码是有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,让主程序等一等它的人民,一切就安好了。

你可能感兴趣的:(MFC 用AfxBeginThread创建线程)