MFC多线程互斥锁的使用
本例演示在MFC中使用多线程。第一部分实现多线程的开启、暂停、继续、注销(见上一篇文章MFC多线程的开启、暂停、继续和注销)。第二部分实现两个线程互斥锁的使用。
演示系统为Win10,平台为VS2017(MFC),主要使用类为CWinThread。
第二部分:
1.在原有基础上(见上一篇文章MFC多线程的开启、暂停、继续和注销)添加一个EDIT控件和一个线程MyThread2。
2.在ThreadTestDlg.h头文件中添加函数及变量声明:
static UINT MyThread2(LPVOID* pParam);//线程函数
CWinThread *Thread2;//线程2
3.在ThreadTestDlg.cpp实现文件上部中添加变量:
CMutex Mutex;//用于互斥锁
修改文件末尾线程1函数定义:
//线程1函数
UINT CThreadTestDlg::MyThread1(LPVOID* pParam)
{
CThreadTestDlg *ap1 = (CThreadTestDlg *)pParam;//获取主类指针,在多线程类中使用主类变量需要使用指针ap1->
CSingleLock singleLock(&Mutex);
while(1)
{
if (ThreadKill)
{
DWORD dwExitCode;//指定线程的退出代码
GetExitCodeThread(ap1->Thread1, &dwExitCode);//获取线程1的退出代码
AfxEndThread(dwExitCode, TRUE);//退出线程
}
else
{
//singleLock.Lock();//没被调用就上锁自己用,已被调用就等着
//if (singleLock.IsLocked())
{
n=1;
strn.Format(_T("%d"), n);
ap1->SetDlgItemText(IDC_EDIT1, strn);
Sleep(30);
}
//singleLock.Unlock();//解锁
}
}
return 0;
}
4.添加线程2函数定义:
UINT CThreadTestDlg::MyThread2(LPVOID* pParam)
{
CThreadTestDlg *ap2 = (CThreadTestDlg *)pParam;//获取主类指针,在多线程类中使用主类变量需要使用指针ap1->
CSingleLock singleLock(&Mutex);
while (1)
{
if (ThreadKill)
{
DWORD dwExitCode;//指定线程的退出代码
GetExitCodeThread(ap2->Thread2, &dwExitCode);//获取线程2的退出代码
AfxEndThread(dwExitCode, TRUE);//退出线程
}
else
{
//singleLock.Lock();//没被调用就上锁自己用,已被调用就等着
//if (singleLock.IsLocked())
{
n=-1;
strn.Format(_T("%d"), n);
ap2->SetDlgItemText(IDC_EDIT2, strn);
Sleep(30);
}
//singleLock.Unlock();//解锁
}
}
return 0;
}
5.修改按钮响应函数:
void CThreadTestDlg::OnBnClickedButton1()
{
n = 0;
ThreadKill = FALSE;
Thread1 = AfxBeginThread((AFX_THREADPROC)MyThread1, this);//启动线程
Thread2 = AfxBeginThread((AFX_THREADPROC)MyThread2, this);//启动线程
}
void CThreadTestDlg::OnBnClickedButton2()
{
Thread1->SuspendThread(); //暂停线程
Thread2->SuspendThread(); //暂停线程
}
void CThreadTestDlg::OnBnClickedButton3()
{
Thread1->ResumeThread(); //继续线程
Thread2->ResumeThread(); //继续线程
}
void CThreadTestDlg::OnBnClickedButton4()
{
ThreadKill = TRUE;//
Thread1->ResumeThread(); //调用继续线程,排除线程处于暂停模式无法注销的情况。若线程处于工作状态,调用此函数无作用。
Thread2->ResumeThread(); //调用继续线程,排除线程处于暂停模式无法注销的情况。若线程处于工作状态,调用此函数无作用。
}
6.编译运行。这是未加互斥锁的情况下,程序中,我们在线程1将n赋值为1并在EDIT1显示;在线程2中将同一个变量n赋值为-1并在EDIT2显示。理论上控件上应分别显示1和-1,但实际上,会出现同时显示1,或同时显示-1或显示-1和1的情况。这是由于线程间同时调用同一变量造成的(严格来讲并不是同时,而是比如线程1赋值为1后还没打印出来就被线程2赋值为-1了,所以造成最后打印出来都是-1,其他情况同理)。
7.互斥锁使用。上面修改中其实已经加入了互斥锁的必要说明,只需要把注销掉的代码复原即可实现。实际操作步骤如下:
①添加类变量声明
CMutex Mutex;//用于互斥锁
②在线程中定义互斥锁类变量
CSingleLock singleLock(&Mutex);
③在线程中添加处理代码
singleLock.Lock();//没被调用就上锁自己用,已被调用就等着
if (singleLock.IsLocked())
{
//在此添加处理代码
}
singleLock.Unlock();//解锁
8.修改后编译运行,可以看到使用互斥锁后,程序按预想的形式运行,EDIT1稳定的显示1,EDIT2稳定的显示-1。
9.使用互斥锁后,当进入线程1时会先检查变量是否被调用,没有的话就上锁给自己用,待自己用完再解锁,如果变量已经被调用上锁了,那么久等待变量释放。这样在一个线程的运行周期内,其他线程就无法更改变量。