此篇讨论一下c++中的线程同步。
线程同步方式:
临界区
事件
互斥量
信号量
为什么要线程同步?
从之前的讨论及实际的项目中,可以看到线程同步给我们带来了很多便利性,但是多线程也同时给我们带来了很多麻烦:一方面,在开发调试阶段带来了很大的难度;另一方面,则需要考虑资源访问的一致性原则。因为在程序中使用多线程的时候,每一个线程并不会各行各事,每个线程之间必然会存在一些联系。如果两个或者多个线程访问同一个独占性的系统资源,这样会导致系统错误。
下面对实现线程同步的几种方法分别介绍
(1) 临界区
临界区是一段独占对某些共享资源的访问代码,在任意时刻只允许一个线程对共享资源访问。如果有多个线程试图同时访问临界区,那么只有一个线程进入临界区,其他试图访问临界区的线程将被挂起,并一直持续到进入临界区的线程离开,其他线程可以继续抢占临界区。
需要说明的是,在使用临界区时,一般不允许其运行时间过长,因为只要进入临界区的线程不离开,其他视图进入此临界区的线程都会挂起,进入等待状态,因此这样在一定程度上影响程序的性能。因此,不要使临界区一直处于未释放的状态。
还需要注意的是,虽然临界区同步速度比较快,但是只能用来同步本进程内的线程,而不能跨进程同步。
MFC中,有一个CCriticalSection类,此类中有两个方法Lock和UnLock,只需要在需要同步的代码前后假如这两个函数,就可以实现临界区资源。
例子如下(部分代码):
CCriticalSection critical_section; char g_array[10]; UINT WriteW(LPVOID pParam) { CEdit *pEdit = (CEdit *)pParam; pEdit->SetWindowText(" "); critical_section.Lock();//锁定临界区 for (int i=0; i<10; i++) { g_array[i]='W'; pEdit->SetWindowText(g_array); Sleep(1000); } critical_section.Unlock(); return 0; } UINT WriteD(LPVOID pParam) { CEdit *pEdit = (CEdit *)pParam; pEdit->SetWindowText(" "); critical_section.Lock(); for (int i=0; i<10; i++) { g_array[i]='D'; pEdit->SetWindowText(g_array); Sleep(1000); } critical_section.Unlock(); return 0; }
创建线程:
void CMthread8Dlg::OnWritew() { // TODO: Add your control notification handler code here CWinThread *pWriteW = AfxBeginThread((AFX_THREADPROC)WriteW, &m_ctrW, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); pWriteW->ResumeThread(); } void CMthread8Dlg::OnWrited() { // TODO: Add your control notification handler code here CWinThread *pWriteD = AfxBeginThread((AFX_THREADPROC)WriteD, &m_ctrD, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); pWriteD->ResumeThread(); }执行结果:
从执行结果看,读的线程只能等到写的线程从临界区出来后才能访问临界区。
(2) 事件
实例参见本博客:c++CreateEvent函数在多线程中使用及实例
此实例中采用CreateEvent 事件函数实现多线程同步的。
也可以采用MFC中的CEvent类实现多线程同步。
部分代码如下:
CEvent event;
char g_array[10];
UINT WriteW(LPVOID pParam)
{
CEdit *pEdit = (CEdit *)pParam;
pEdit->SetWindowText(" ");
for (int i=0; i<10; i++)
{
g_array[i] = 'W';
pEdit->SetWindowText(g_array);
Sleep(200);
}
event.SetEvent();
return 0;
}
UINT WriteD(LPVOID pParam)
{
CEdit *pEdit = (CEdit *)pParam;
pEdit->SetWindowText(" ");
WaitForSingleObject(event,INFINITE);
for (int i=0; i<10; i++)
{
g_array[i] = 'W';
pEdit->SetWindowText(g_array);
Sleep(200);
}
return 0;
}
创建线程的代码和临界区中显示的代码一样。
主要通过WaitForSingleObject函数实现多线程的同步。此函数等待上一个线程释放事件,即事件变成有信号状态时,才执行此线程下面的程序。