工作积累之线程同步

 工作中需要在一个线程A中控制另外一个线程B的运行、暂停和取消暂停(继续运行)、停止,涉及到线程同步问题;以前需要线程同步时都是从网上找现成的代码,从未自己认真研究过其中的原理,今天就认真的总结一下。有关线程同步互斥的控制方法,可以参考上一篇文章,这里直接叙述实现的代码。

实现上述的同步,使用了两个事件(Event)和两个互斥量(Mutex),分别为:

/** Events 事件 */ 
HANDLE      m_heventUnpaused;      //暂停
HANDLE      m_heventThreadDead;    //停止

/** Mutexes 互斥量*/
HANDLE      m_hThreadRunningMutex;      //运行
HANDLE      m_hGrabbingPauseFlagMutex;  //暂停
还有两个布尔变量,分别用来控制运行于暂停,是上面的两个互斥量保护的资源,如下:
bool       m_bPaused;              //暂停
bool       m_bThreadRun;           //运行

先把上面各个变量初始化一下:
m_heventUnpaused     = CreateEvent( NULL, FALSE, FALSE, NULL );  //将暂停事件初始化为无信号状态
m_heventThreadDead     = CreateEvent( NULL, FALSE, FALSE, NULL ); 

m_hThreadRunningMutex      = ::CreateMutex( NULL, FALSE, NULL );
m_hGrabbingPauseFlagMutex  = ::CreateMutex( NULL, FALSE, NULL );    //将暂停互斥量初始化为无线程拥有该互斥量状态

m_bPaused      = false;
m_bThreadRun = false;


1. 首先是A线程如何控制B线程的运行:
下面的两段代码分别展示了如何使用互斥量来同步对变量m_bThreadRun的读写操作:
BOOL threadFlag()   //读操作
{
BOOL returnValue = false;
DWORD dwRet = ::WaitForSingleObject( m_hThreadRunningMutex, 1000 );
if( dwRet == WAIT_OBJECT_0 )
{
returnValue = m_bThreadRun;
}


::ReleaseMutex( m_hThreadRunningMutex );


return returnValue;
};
 void setThreadRunningFlag( bool trueOrFalse )   //写操作
{
DWORD dwRet = ::WaitForSingleObject( m_hThreadRunningMutex, 5000 );  
if( dwRet == WAIT_OBJECT_0 )
{
m_bThreadRun = trueOrFalse;
}
::ReleaseMutex( m_hThreadRunningMutex );
return;
};


然后在A线程中创建B线程,并调用setThreadRunningFlag(true);代码:
setThreadRunningFlag( true );
AfxBeginThread( threadGrab, this );
再在B线程函数中循环调用threadFlag(),B线程就运行起来了,代码:
UINT threadBFunction(void* pParam)
{
    while( threadFlag() )
    {
       ...
    }
    ...
}


2.如何在A线程中控制B线程的暂停与取消暂停;
下面的两段代码分别展示了如何使用互斥量来同步对变量m_bPaused的读写操作:
BOOL grabbingPaused()
{
BOOL returnValue = false;


DWORD dwRet = ::WaitForSingleObject( m_hGrabbingPauseFlagMutex, 5000 );
if( dwRet == WAIT_OBJECT_0 )
{
returnValue = m_bPaused;
}


::ReleaseMutex( m_hGrabbingPauseFlagMutex );


return returnValue;
};
void setGrabbingPausedFlag( bool trueOrFalse )
{
DWORD dwRet = ::WaitForSingleObject( m_hGrabbingPauseFlagMutex, 1000 );
if( dwRet == WAIT_OBJECT_0 )
{
m_bPaused = trueOrFalse;
}


::ReleaseMutex( m_hGrabbingPauseFlagMutex );


return;
};


初始时变量m_bPaused设为false;
m_bPaused = false;
然后在B线程函数中有如下代码:
UINT threadBFunction(void* pParam)
{
    while( threadFlag() )
    {
        if( grabbingPaused() )
     {
           DWORD dw = WaitForSingleObject( m_heventUnpaused, INFINITE );
       ASSERT( dw == WAIT_OBJECT_0  );  
     }
     ...  //计算处理
    }
    ...
}


因为m_bPaused=false,所以代码是无法执行到if语句中去的;一直在执行“计算处理”的代码;当在A线程中调用了PauseCapture函数时,
B线程的执行就会暂停下来,我们先看看PauseCapture函数的代码:
bool PauseCapture()
{
if ( grabbingPaused() )
{
setGrabbingPausedFlag ( false );
}
else
{
setGrabbingPausedFlag ( true );
}


if( !grabbingPaused() )
{
SetEvent( m_heventUnpaused );     
}
return true;
}


看到了吧,此时调用PauseCapture函数,会首先执行setGrabbingPausedFlag ( true );语句,把m_bPaused置为true;此时在B线程函数中就
可以进入if语句了;然后if语句中有WaitForSingleObject( m_heventUnpaused, INFINITE );这一行代码,大家还记得初始化时,
m_heventUnpaused被初始化成为了无信号状态,所以B线程就在这里无限期等待m_heventUnpaused变为有信号状态;B线程就暂停了;
当用户再次调用PauseCapture函数,会首先执行setGrabbingPausedFlag ( false );语句,把m_bPaused置为false;然后由于m_bPaused是
false,语句SetEvent( m_heventUnpaused );被调用,m_heventUnpaused就变为有信号状态了,然后B线程就可以继续往下执行了。
3.如何在A线程中控制B线程的停止;
调用函数StopCapture来停止B线程;
bool StopCapture( void)
{
// Inform the grab thread to quit
if ( threadFlag() )
{
setThreadRunningFlag ( false );
DWORD dwRet = WaitForSingleObject( m_heventThreadDead, 5000 );
ASSERT( dwRet == WAIT_OBJECT_0 );
}
...
}


setThreadRunningFlag ( false );使得B线程函数中的while( threadFlag() )条件不成立,退出循环;
B线程函数:重点是最后一行代码;SetEvent( m_heventThreadDead );将m_heventThreadDead事件置为有信号状态;
UINT threadBFunction(void* pParam)
{
    while( threadFlag() )
    {
        if( grabbingPaused() )
     {
           DWORD dw = WaitForSingleObject( m_heventUnpaused, INFINITE );
       ASSERT( dw == WAIT_OBJECT_0  );  
     }
     ...  //计算处理
    }
    SetEvent( m_heventThreadDead );
}


然后在StopCapture函数中又调用了WaitForSingleObject( m_heventThreadDead, 5000 );ASSERT( dwRet == WAIT_OBJECT_0 );两条语句,
来确认B线程已经结束;

你可能感兴趣的:(工作积累之线程同步)