工作中需要在一个线程A中控制另外一个线程B的运行、暂停和取消暂停(继续运行)、停止,涉及到线程同步问题;以前需要线程同步时都是从网上找现成的代码,从未自己认真研究过其中的原理,今天就认真的总结一下。有关线程同步互斥的控制方法,可以参考上一篇文章,这里直接叙述实现的代码。
1. 首先是A线程如何控制B线程的运行:
下面的两段代码分别展示了如何使用互斥量来同步对变量m_bThreadRun的读写操作:
然后在A线程中创建B线程,并调用setThreadRunningFlag(true);代码:
setThreadRunningFlag( true );
AfxBeginThread( threadGrab, this );
再在B线程函数中循环调用threadFlag(),B线程就运行起来了,代码:
2.如何在A线程中控制B线程的暂停与取消暂停;
下面的两段代码分别展示了如何使用互斥量来同步对变量m_bPaused的读写操作:
初始时变量m_bPaused设为false;
m_bPaused = false;
然后在B线程函数中有如下代码:
因为m_bPaused=false,所以代码是无法执行到if语句中去的;一直在执行“计算处理”的代码;当在A线程中调用了PauseCapture函数时,
B线程的执行就会暂停下来,我们先看看PauseCapture函数的代码:
看到了吧,此时调用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线程;
setThreadRunningFlag ( false );使得B线程函数中的while( threadFlag() )条件不成立,退出循环;
B线程函数:重点是最后一行代码;SetEvent( m_heventThreadDead );将m_heventThreadDead事件置为有信号状态;
然后在StopCapture函数中又调用了WaitForSingleObject( m_heventThreadDead, 5000 );ASSERT( dwRet == WAIT_OBJECT_0 );两条语句,
来确认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线程已经结束;