四、MFC多线程间通信
1.线程之间的通信简介
一般而言,在一个应用程序中(即进程),一个线程往往不是孤立存在的,常常需要和其它线程通信,以执行特定的任务。如主线程和次线程,次线程与次线程,工作线程和用户界面线程等。这样,线程与线程间必定有一个信息传递的渠道。这种线程间的通信不但是难以避免的,而且在多线程编程中也是复杂和频繁的。线程间的通信涉及到4个问题:
(1) 线程间如何传递信息
(2) 线程之间如何同步,以使一个线程的活动不会破坏另一个线程的活动,以保证计算结果的正确合理
(3) 当线程间具有依赖关系时,如何调度多个线程的处理顺序
(4) 如何避免死锁问题
在windows系统中线程间的通信一般采用四种方式:全局变量方式、消息传递方式、参数传递方式和线程同步法。下面分别作介绍。
2.全局变量方式
由于属于同一个进程的各个线程共享操作系统分配该进程的资源,故解决线程间通信最简单的一种方法是使用全局变量。对于标准类型的全局变量,我们建议使用volatile 修饰符,它告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变。
实例演示
该实例采用全局变量来控制时间显示线程的显示格式,比较简单。
主要的代码如下:
.h头文件
//线程函数声明
DWORD WINAPIThreadProc(LPVOIDlpParam);
protected:
HANDLE m_hThread;//线程句柄
DWORD m_nThread;//线程ID
.cpp实现文件
volatileBYTE m_nShowFlag = 31;//定义全局变量,用于控制显示时间的格式。volatile 修饰符的作用是告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中。
//创建显示时间的线程,参数无
m_hThread =CreateThread(NULL,0,ThreadProc,NULL,0,&m_nThread);
//线程执行函数,用于实时显示时间,并按规定格式显示
DWORD WINAPIThreadProc(LPVOID lpParam)
{
while(m_nShowFlag)
{
CTime time;
CString strTime,strFormat;
time=CTime::GetCurrentTime();
strFormat = “%H:%M”;
if (m_nShowFlag&2)
{//日期
strFormat = “%Y-%m-%d” + strFormat;
}
if (m_nShowFlag&4)
{//秒钟
strFormat += “:%S”;
}
if (m_nShowFlag&8)
{//周数
strFormat += “%W”;
}
if (m_nShowFlag&16)
{//星期
strFormat += “%a”;
}
strTime=time.Format(strFormat);
::SetDlgItemText(AfxGetApp()-》m_pMainWnd-》m_hWnd,IDC_STATIC_TIME,strTime);
Sleep(100);
}
return 0;
}
运行效果:
工程源码下载地址:
http://download.csdn.net/detail/cbnotes/4962315
注意事项:
(1) 全局变量最好放在.CPP文件的起始处,而不要放在.h头文件中,否则将出现重复链接的编译错误。定义全局变量时最好显式初始化,默认初始值为零。
(2) 注意语句
::SetDlgItemText(AfxGetMainWnd()-》m_hWndIDC_STATIC_TIME,strTime);在VC6.0中可以通过,但在VC2008却出错,这是因为在VC2008中不支持AfxGetMainWnd()-》m_hWnd来获取HWND,但可以采AfxGetApp()-》m_pMainWnd-》m_hWnd来获取。所以上面的语句更改为:
::SetDlgItemText(AfxGetApp()-》m_pMainWnd-》m_hWnd,IDC_STATIC_TIME,strTime);
(3) 用全局变量方式来实现多线程的通信比较简单实用,单注意最好不要多个线程对它进行修改,否则将可能出错,这将在后面会具体讲解。
3.参数传递方式
该方式是线程通信的官方标准方法,多数情况下,主线程创建子线程并让其子线程为其完成特定的任务,主线程在创建子线程时,可以通过传给线程函数的参数和其通信,三类创建线程的函数都支持参数的传递(哪三类?看前面的介绍吧!)。所传递的参数是一个32位的指针,该指针不但可以指向简单的数据,而且可以指向结构体或类等复杂的抽象数据类型。
实例演示
下面将分别简单演示三类创建线程时提供参数传递的方法。
主要的代码如下:
.h头文件
//线程函数声明
DWORD WINAPI ThreadFunc1(LPVOID lpParam);//线程函数
void ThreadFunc2(void *pArg); //线程函数
UINT ThreadFunc3(LPVOID lpParam);//线程函数
//全局函数
POINT GetRandPoint();//得到随机点的坐标
//结构体定义,用于向线程传递多个参数
struct threadInfo
{
HWND hWnd;//主窗口句柄
COLORREF clrPen;//画笔颜色
};
.cpp实现文件
//开始创建线程:创建个线程
void CMultThreadComm2Dlg::OnStart(void)
{
//线程:使用win32 API 创建:实时显示时间
m_hThread1 = CreateThread(NULL,0,ThreadFunc1,&m_stTime.m_hWnd,0,NULL);//
//线程:使用CRT 创建:随机画线
m_info.hWnd = m_hWnd;
m_info.clrPen =RGB(255,0,0);
_beginthread(ThreadFunc2,0,&m_info);
//线程:使用MFC线程函数创建:显示进度
m_pThread = AfxBeginThread(ThreadFunc3,&m_ctrlProgress);
}
//停止/开始
void CMultThreadComm2Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString szTitle;
GetDlgItemText(IDC_BUTTON1,szTitle);
if (szTitle == “停止”)
{//停止
g_bRun = false;
SetDlgItemText(IDC_BUTTON1,“开始”);
}
else
{//开始
g_bRun = true;
OnStart();
SetDlgItemText(IDC_BUTTON1,“停止”);
}
}
//线程执行函数:实时显示当前的时间
DWORD WINAPI ThreadFunc1(LPVOID lpParam)
{
HWND *hWnd = (HWND*)lpParam;
//CWnd *pWnd = AfxGetApp()-》m_pMainWnd;//当没有传递参数时,可以用该api实现
CWnd *pWnd = CWnd::FromHandle(*hWnd);
char tmpbuf[128] ={‘0’};
time_t t;
while(g_bRun)
{
t = time(NULL);
strftime(tmpbuf,128,“%Y-%m-%d%a %I:%M:%S %p”,localtime(&t));
pWnd-》SetWindowText(tmpbuf);
Sleep(500);
}
return 0;
}
//线程执行函数:随机画线
void ThreadFunc2(void *pArg)
{
threadInfo *threadinfo= (threadInfo*)pArg;
CWnd *pWnd = CWnd::FromHandle(threadinfo-》hWnd);
CDC *pDC = pWnd-》GetDC();
CPen pen(PS_SOLID,2,threadinfo-》clrPen);
pDC-》SelectObject(&pen);
pDC-》SetROP2(R2_NOTXORPEN);
while(g_bRun)
{
POINT StartPos= GetRandPoint();
POINT EndPos = GetRandPoint();
//
CString str;
str.Format(“%d,%d : %d,%d\n”,StartPos.x,StartPos.y,EndPos.x,EndPos.y);
TRACE(str);
pDC-》MoveTo(StartPos);
pDC-》LineTo(EndPos);
Sleep(100);
pDC-》MoveTo(StartPos);
pDC-》LineTo(EndPos);
Sleep(100);
}
DeleteObject(pDC);
}
//线程执行函数:显示进度
UINT ThreadFunc3(LPVOIDlpParam)
{
CProgressCtrl *pProgress= (CProgressCtrl*)lpParam;
while(g_bRun)
{
pProgress-》StepIt();
Sleep(500);
}
return 0;
}
//得到随机点
POINT GetRandPoint()
{
POINT Point;
Point.x = rand()%439;
Point.y = rand()%208;
return Point;
}
运行效果:
工程源码下载地址:
http://download.csdn.net/detail/cbnotes/4984274
注意事项:
(1) 注意三类线程的创建方法和各自的线程函数的格式(返回类型各不一样)。
(2) 注意三类参数的传递:单个参数、多参数(结构体),复杂参数(类)。
(3) 采用参数传递方式进行线程间的通信只适用于主线程向从线程的通信。
(4) 不知道大家看出该程序的一个BUG没有(大家可以下载工程源码,编译并运行,可以很明显的发现。),就是线程二的随机画线线程,原意是随机画线并清除,但运行发现第一次画线时总是没有被清除掉,为什么?望大家动脑,知道的可以留言,大家一起学习和讨论!
评论
查看更多