在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects

http://blog.csdn.net/wishfly/article/details/3726985

你在主线程用了WaitForSingleObject,导致了消息循环的阻塞,界面假死。

然后在线程中调用了SetDlgItemText,而SetDlgItemText实际上调用的是SendMessage,

而SendMessage要等待主线程处理完毕发送的消息才返回继续执行,

而你主线程的消息循环已经阻塞,无法处理消息,导致整个过程“我等你,你等我”,无穷下去

在界面线程不能使用Sleep和WaitForSingleObject之类的函数,

比较好的方法是,将句柄作为线程参数传递进线程,

当线程结束后发一条消息通知主线程处理完毕

在主线程中慎用WaitForSingleObject (WaitForMultipleObjects)

下面的代码我调试了将近一个星期,你能够看出什么地方出了问题吗?

线程函数:

在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects
DWORD WINAPI ThreadProc(
while(!bTerminate)
{
// 从一个链表中读取信息并且插入到CListCtrl中
// CListCtrl的句柄是通过线程参数传递进来的
for(;;)
{
ReadInfoFromList();
InsertToCListCtrl();
}
}
}
在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects

主线程中使用CreateThread启动线程。当想终止子线程时,在主线程中:

bTerminate = TRUE;
WaitForSingleObject(threadHandle, INFINITE);

可是,以运行到WaitForSingleObject,子线程就Crash了。为什么呢?

问题原因:

后来我终于在InsertItem的反汇编中发现了如下的代码
call dword ptr [__imp__SendMessageA@16 (7C141B54h)]
可见,InsertItem是必须借助消息循环来完成任务的。

如果我们在主线程中WaitForSingleObject了,必然导致主线程阻塞,

也就导致了消息循环的阻塞,最终导致工作线程Crash掉了*_*

解决方案:

为了解决在主线程中Wait的问题,微软专门设计了一个函数MsgWaitForMultipleObjects,

这个函数即可以等待信号(thread,event,mutex等等),也可以等待消息(MSG)。

即不论有信号被激发或者有消息到来,此函数都可以返回。呵呵,那么我的解决办法也就出来了。

将上面的WaitForSingleObject用下面的代码替换:

在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects
while(TRUE)
{ DWORD result ;
MSG msg ; result = MsgWaitForMultipleObjects(1, &readThreadHandle,
FALSE, INFINITE, QS_ALLINPUT); if (result == (WAIT_OBJECT_0))
{
break;
}
else
{
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
DispatchMessage(&msg);
}
}
在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects

总结:

如果在工作线程中有可能涉及到了消息驱动的API,

那么不能在主线程中使用WaitForSingleObject一类函数,而必须使用上述的方案。

http://www.midnightbeach.com/jon/pubs/MsgWaits/SynchedThreads_pas.html

在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects
unit SynchedThreads;

// Copyright © 1998 by Jon Shemitz, all rights reserved.
// Permission is hereby granted to freely use, modify, and
// distribute this source code PROVIDED that all six lines of
// this copyright and contact notice are included without any
// changes. Questions? Comments? Offers of work?
// mailto:jon@midnightbeach.com - http://www.midnightbeach.com {$T+} {$hints on} {$warnings on} interface uses Windows, Classes, SysUtils, Forms; // Simple threads type
TThreadMethod = procedure( Data : pointer ) of object; TSimpleThread = class( TThread )
public
constructor CreateSimple( CreateSuspended : boolean;
_Action : TThreadMethod; _Data : pointer );
procedure AbortThread;
protected
ThreadMethod : TThreadMethod;
Data : pointer; private
procedure Execute; override;
end; function RunInThread( Handler : TThreadMethod; Data : pointer ) : TSimpleThread; // Wait threads (basic synchronization) procedure MsgWaitForSingleObject( Handle : THandle );
function SpawnProcess( const Command : string ) : TProcessInformation; type
TWaitThread = class( TSimpleThread )
public
constructor CreateWait( _Action : TThreadMethod; _Data : pointer );
procedure WaitFor;
procedure MsgWaitFor;
procedure AbortThread;
private
AbortFlag : ^boolean;
procedure Run( MsgWait : boolean );
end; procedure WaitForThread( Handler : TThreadMethod; Data : pointer );
procedure MsgWaitForThread( var Thread : TWaitThread; Handler : TThreadMethod;
Data : pointer ); // Stop/start threads type
EAbortedThread = class( Exception )
end; EThreadInUse = class( Exception )
end; TStopStartThread = class( TSimpleThread )
public
Waiting : boolean;
constructor Create;
procedure WaitFor( _Action : TThreadMethod; _Data : pointer );
procedure MsgWaitFor( _Action : TThreadMethod; _Data : pointer );
procedure AbortThread;
private
Event : THandle;
Aborted : boolean;
destructor Destroy; override;
procedure Execute; override;
procedure Run( _Action : TThreadMethod; _Data : pointer;
MsgWait : boolean );
end; implementation // TSimpleThread, RunInThread constructor TSimpleThread.CreateSimple( CreateSuspended : boolean;
_Action : TThreadMethod; _Data : pointer );
begin
ThreadMethod := _Action; // Set these BEFORE calling
Data := _Data; // inherited Create()!
FreeOnTerminate := True;
inherited Create( CreateSuspended );
end; // TSimpleThread.Create procedure TSimpleThread.Execute;
begin
ThreadMethod( Data );
end; // TSimpleThread.Execute procedure TSimpleThread.AbortThread;
begin
Suspend; // // Can't kill a running thread by Freeing it
Free; // Kills thread
end; // TSimpleThread.AbortThread function RunInThread( Handler : TThreadMethod; Data : pointer ) : TSimpleThread;
begin
Result := TSimpleThread.CreateSimple( False, Handler, Data );
end; // RunInThread // Basic synchronization procedure MsgWaitForSingleObject( Handle : THandle );
begin
repeat
if MsgWaitForMultipleObjects( 1, Handle, False, INFINITE, QS_ALLINPUT )
= WAIT_OBJECT_0 + 1 then
Application.ProcessMessages
else
BREAK;
until True = False;
end; // MsgWaitForSingleObject
function SpawnProcess( const Command : string ) : TProcessInformation;
var
StartupInfo : TStartupInfo;
begin
FillChar( StartupInfo, SizeOf( StartupInfo ), 0 ); // use defaults
StartupInfo.cb := SizeOf( StartupInfo );
CreateProcess( nil, PChar( Command ), nil, nil, False, 0, nil, nil,
StartupInfo, Result );
end; // SpawnProcess constructor TWaitThread.CreateWait( _Action : TThreadMethod; _Data : pointer );
begin
CreateSimple( True, _Action, _Data ); // CreateSuspended
AbortFlag := nil;
end; // TWaitThread.CreateWait procedure TWaitThread.WaitFor;
begin
Run( False );
end; // TWaitThread.WaitFor procedure TWaitThread.MsgWaitFor;
begin
Run( True );
end; // TWaitThread.MsgWaitFor procedure TWaitThread.Run( MsgWait : boolean );
var
Aborted : boolean;
begin
AbortFlag := @Aborted;
Aborted := False;
Resume;
if MsgWait then
MsgWaitForSingleObject
( Handle )
else
inherited WaitFor;
if Aborted then
Abort;
end; // TWaitThread.Run procedure TWaitThread.AbortThread;
begin
Assert( Assigned( AbortFlag ) );
AbortFlag^ := True;
inherited;
end; // TWaitThread.CreateWait procedure WaitForThread( Handler : TThreadMethod; Data : pointer );
begin
TWaitThread.CreateWait( Handler, Data ).WaitFor;
end; // WaitForThread procedure MsgWaitForThread( var Thread : TWaitThread; Handler : TThreadMethod;
Data : pointer );
begin
Thread := TWaitThread.CreateWait( Handler, Data );
Thread.MsgWaitFor;
Thread := nil;
end; // MsgWaitForThread // Stop/start threads constructor TStopStartThread.Create;
begin
Event := CreateEvent( nil, True, False, nil );
// API call is smaller and simpler than Delphi wrapper
Assert( Event <> NULL );
Waiting := False;
Aborted := False;
inherited Create( True ); // Create a suspended thread
end; // TStopStartThread.Create destructor TStopStartThread.Destroy;
begin
CloseHandle( Event );
inherited;
end; // TStopStartThread.Destroy procedure TStopStartThread.Execute;
begin
while not Terminated do
begin
Assert( Assigned( ThreadMethod ) );
ThreadMethod( Data );
SetEvent( Event );
Suspend;
end;
end; // TStopStartThread.Execute procedure TStopStartThread.Run( _Action : TThreadMethod; _Data : pointer;
MsgWait : boolean );
begin
if Waiting then
raise EThreadInUse.Create( 'Thread in use' );
if Aborted then
raise EAbortedThread.Create( 'Aborted thread' ); ThreadMethod := _Action;
Data := _Data;
Waiting := True;
ResetEvent( Event );
Resume;
if MsgWait then
MsgWaitForSingleObject
( Event )
else
WaitForSingleObject( Event, INFINITE );
Waiting := False;
if Aborted then
Abort; // Raise an EAbort exception
end; // TStopStartThread.InternalRun procedure TStopStartThread.MsgWaitFor( _Action : TThreadMethod; _Data : pointer );
begin
Run( _Action, _Data, True );
end; // TStopStartThread.Run procedure TStopStartThread.WaitFor( _Action : TThreadMethod; _Data : pointer );
begin
Run( _Action, _Data, False );
end; // TStopStartThread.RunBlocking procedure TStopStartThread.AbortThread;
begin
Suspend; // // Can't kill a running thread by Freeing it
Aborted := True;
SetEvent( Event );
end; // TStopStartThread.AbortThread end.
在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects
在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects
// Copyright (C) 2003-2009 Dolphin Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/ // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ #include "Setup.h"
#include "Thread.h"
#include "Log.h"
#ifdef SETUP_TIMER_WAITING
#include <windows.h>
#include "ConsoleWindow.h"
EventCallBack FunctionPointer[ 10 ];
#endif namespace Common
{
#ifdef _WIN32 void InitThreading ( )
{
// Nothing to do in Win32 build.
} CriticalSection::CriticalSection ( int spincount )
{
if ( spincount )
{
InitializeCriticalSectionAndSpinCount ( &section, spincount );
}
else
{
InitializeCriticalSection ( &section );
}
} CriticalSection::~CriticalSection ( )
{
DeleteCriticalSection ( &section );
} void CriticalSection::Enter ( )
{
EnterCriticalSection ( &section );
} bool CriticalSection::TryEnter ( )
{
return TryEnterCriticalSection ( &section ) ? true : false;
} void CriticalSection::Leave ( )
{
LeaveCriticalSection ( &section );
} Thread::Thread ( ThreadFunc function, void* arg )
: m_hThread ( NULL ), m_threadId( 0 )
{
m_hThread = CreateThread ( 0, // Security attributes
0, // Stack size
function, arg, 0, &m_threadId );
} Thread::~Thread ( )
{
WaitForDeath ( );
} void Thread::WaitForDeath ( const int _Wait )
{
if ( m_hThread )
{
WaitForSingleObject ( m_hThread, _Wait );
CloseHandle ( m_hThread );
m_hThread = NULL;
}
} void Thread::SetAffinity ( int mask )
{
SetThreadAffinityMask ( m_hThread, mask );
} void Thread::SetCurrentThreadAffinity ( int mask )
{
SetThreadAffinityMask ( GetCurrentThread( ), mask );
} // Regular same thread loop based waiting
Event::Event ( )
{
m_hEvent = 0;
#ifdef SETUP_TIMER_WAITING
DoneWaiting = false;
StartWait = false;
hTimer = NULL;
hTimerQueue = NULL;
#endif
} void Event::Init ( )
{
m_hEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
} void Event::Shutdown ( )
{
CloseHandle ( m_hEvent );
m_hEvent = 0;
} void Event::Set ( )
{
SetEvent ( m_hEvent );
} void Event::Wait ( )
{
WaitForSingleObject ( m_hEvent, INFINITE );
} inline HRESULT MsgWaitForSingleObject ( HANDLE handle, DWORD timeout )
{
return MsgWaitForMultipleObjects ( 1, &handle, FALSE, timeout, 0 );
} void Event::MsgWait ( )
{
// Adapted from MSDN example http://msdn.microsoft.com/en-us/library/ms687060.aspx
while ( true )
{
DWORD result;
MSG msg;
// Read all of the messages in this next loop,
// removing each message as we read it.
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
// If it is a quit message, exit.
if ( msg.message == WM_QUIT )
return;
// Otherwise, dispatch the message.
DispatchMessage ( &msg );
} // Wait for any message sent or posted to this queue
// or for one of the passed handles be set to signaled.
result = MsgWaitForSingleObject ( m_hEvent, THREAD_WAIT_TIMEOUT ); // The result tells us the type of event we have.
if ( result == ( WAIT_OBJECT_0 + 1 ) )
{
// New messages have arrived.
// Continue to the top of the always while loop to
// dispatch them and resume waiting.
continue;
}
else
{
// result == WAIT_OBJECT_0
// Our event got signaled
return;
}
}
} /* Separate thread timer based waiting, instead of same thread loop waiting. The downside with this
is that it's less convenient to use because we can't stall any threads with a loop. The positive
is that we don't cause these incredibly annoying WaitForEternity() hangings. */
#ifdef SETUP_TIMER_WAITING /* I could not figure out how to place this in the class to, CreateTimerQueueTimer() would complain
about some kind of type casting, anyone have any ideas about how to do it? */
VOID CALLBACK TimerRoutine ( PVOID lpParam, BOOLEAN TimerOrWaitFired )
{
if ( lpParam == NULL )
{
DEBUG_LOG ( CONSOLE, "TimerRoutine lpParam is NULL\n" );
}
else
{
// lpParam points to the argument; in this case it is an int // DEBUG_LOG(CONSOLE, "Timer[%i] will call back\n", *(int*)lpParam);
} // Call back
int Id = *( int* )lpParam;
if ( FunctionPointer[ Id ] )
FunctionPointer[ Id ]( );
} // Create a timer that will call back to the calling function
bool Event::TimerWait ( EventCallBack WaitCB, int _Id, bool OptCondition )
{
Id = _Id; // DEBUG_LOG(CONSOLE, "TimerWait[%i]: %i %i %i\n", Id, StartWait, DoneWaiting, OptCondition); FunctionPointer[ Id ] = WaitCB; // This means we are done waiting, so we wont call back again, and we also reset the variables for this Event
if ( DoneWaiting && OptCondition )
{
StartWait = false;
DoneWaiting = false;
FunctionPointer[ Id ] = NULL; // Delete all timers in the timer queue.
if ( !DeleteTimerQueue( hTimerQueue ) )
DEBUG_LOG ( CONSOLE, "DeleteTimerQueue failed (%d)\n",
GetLastError( ) ); hTimer = NULL;
hTimerQueue = NULL; return true;
} // Else start a new callback timer
StartWait = true; // Create the timer queue if needed
if ( !hTimerQueue )
{
hTimerQueue = CreateTimerQueue ( );
if ( NULL == hTimerQueue )
{
DEBUG_LOG ( CONSOLE, "CreateTimerQueue failed (%d)\n",
GetLastError( ) );
return false;
}
} // Set a timer to call the timer routine in 10 seconds.
if ( !CreateTimerQueueTimer( &hTimer, hTimerQueue,
( WAITORTIMERCALLBACK )TimerRoutine, &Id, 10, 0, 0 ) )
{
DEBUG_LOG ( CONSOLE, "CreateTimerQueueTimer failed (%d)\n",
GetLastError( ) );
return false;
} return false;
} // Check if we are done or not
bool Event::DoneWait ( )
{
if ( StartWait && DoneWaiting )
return true;
else
return false;
} // Tells the timer that we are done waiting
void Event::SetTimer ( )
{
// We can not be done before we have started waiting
if ( StartWait )
DoneWaiting = true;
}
#endif // Supporting functions
void SleepCurrentThread ( int ms )
{
Sleep ( ms );
} typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space)
DWORD dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO;
// Usage: SetThreadName (-1, "MainThread");
//
// Sets the debugger-visible name of the current thread.
// Uses undocumented (actually, it is now documented) trick.
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp void SetCurrentThreadName ( const TCHAR* szThreadName )
{
THREADNAME_INFO info;
info.dwType = 0x1000;
#ifdef UNICODE
// TODO: Find the proper way to do this.
char tname[ 256 ];
unsigned int i; for ( i = 0; i < _tcslen( szThreadName ); i++ )
{
tname[ i ] = ( char )szThreadName[ i ];
// poor man's unicode->ansi, TODO: fix
} tname[ i ] = 0;
info.szName = tname;
#else
info.szName = szThreadName;
#endif info.dwThreadID = -1; // dwThreadID;
info.dwFlags = 0;
__try
{
RaiseException ( 0x406D1388, 0, sizeof( info ) / sizeof( DWORD ),
( ULONG_PTR* )&info );
}
__except ( EXCEPTION_CONTINUE_EXECUTION )
{
}
} // TODO: check if ever inline
LONG SyncInterlockedIncrement ( LONG *Dest )
{
return InterlockedIncrement ( Dest );
} LONG SyncInterlockedExchangeAdd ( LONG *Dest, LONG Val )
{
return InterlockedExchangeAdd ( Dest, Val );
} LONG SyncInterlockedExchange ( LONG *Dest, LONG Val )
{
return InterlockedExchange ( Dest, Val );
} #else // !WIN32, so must be POSIX threads pthread_key_t threadname_key; CriticalSection::CriticalSection ( int spincount_unused )
{
pthread_mutex_init ( &mutex, NULL );
} CriticalSection::~CriticalSection ( )
{
pthread_mutex_destroy ( &mutex );
} void CriticalSection::Enter ( )
{
int ret = pthread_mutex_lock ( &mutex );
if ( ret )
ERROR_LOG ( COMMON, "%s: pthread_mutex_lock(%p) failed: %s\n",
__FUNCTION__, &mutex, strerror( ret ) );
} bool CriticalSection::TryEnter ( )
{
return ( !pthread_mutex_trylock( &mutex ) );
} void CriticalSection::Leave ( )
{
int ret = pthread_mutex_unlock ( &mutex );
if ( ret )
ERROR_LOG ( COMMON, "%s: pthread_mutex_unlock(%p) failed: %s\n",
__FUNCTION__, &mutex, strerror( ret ) );
} Thread::Thread ( ThreadFunc function, void* arg ) : thread_id ( 0 )
{
pthread_attr_t attr;
pthread_attr_init ( &attr );
pthread_attr_setstacksize ( &attr, 1024 * 1024 );
int ret = pthread_create ( &thread_id, &attr, function, arg );
if ( ret )
ERROR_LOG ( COMMON, "%s: pthread_create(%p, %p, %p, %p) failed: %s\n",
__FUNCTION__, &thread_id, &attr, function, arg, strerror( ret ) ); INFO_LOG ( COMMON, "created new thread %lu (func=%p, arg=%p)\n", thread_id,
function, arg );
} Thread::~Thread ( )
{
WaitForDeath ( );
} void Thread::WaitForDeath ( )
{
if ( thread_id )
{
void* exit_status;
int ret = pthread_join ( thread_id, &exit_status );
if ( ret )
ERROR_LOG ( COMMON, "error joining thread %lu: %s\n", thread_id,
strerror( ret ) );
if ( exit_status )
ERROR_LOG ( COMMON, "thread %lu exited with status %d\n", thread_id,
*( int * )exit_status );
thread_id = 0;
}
} void Thread::SetAffinity ( int mask )
{
// This is non-standard
#ifdef __linux__
cpu_set_t cpu_set;
CPU_ZERO ( &cpu_set ); for ( unsigned int i = 0; i < sizeof( mask ) * 8; i++ )
{
if ( ( mask >> i ) & 1 )
{
CPU_SET ( i, &cpu_set );
}
} pthread_setaffinity_np ( thread_id, sizeof( cpu_set ), &cpu_set );
#endif
} void Thread::SetCurrentThreadAffinity ( int mask )
{
#ifdef __linux__
cpu_set_t cpu_set;
CPU_ZERO ( &cpu_set ); for ( size_t i = 0; i < sizeof( mask ) * 8; i++ )
{
if ( ( mask >> i ) & 1 )
{
CPU_SET ( i, &cpu_set );
}
} pthread_setaffinity_np ( pthread_self( ), sizeof( cpu_set ), &cpu_set );
#endif
} void InitThreading ( )
{
static int thread_init_done = 0;
if ( thread_init_done )
return; if ( pthread_key_create( &threadname_key, NULL /* free */ ) != 0 )
perror ( "Unable to create thread name key: " ); thread_init_done++;
} void SleepCurrentThread ( int ms )
{
usleep ( 1000 * ms );
} void SetCurrentThreadName ( const TCHAR* szThreadName )
{
pthread_setspecific ( threadname_key, strdup( szThreadName ) );
INFO_LOG ( COMMON, "%s(%s)\n", __FUNCTION__, szThreadName );
} Event::Event ( )
{
is_set_ = false;
} void Event::Init ( )
{
pthread_cond_init ( &event_, 0 );
pthread_mutex_init ( &mutex_, 0 );
} void Event::Shutdown ( )
{
pthread_mutex_destroy ( &mutex_ );
pthread_cond_destroy ( &event_ );
} void Event::Set ( )
{
pthread_mutex_lock ( &mutex_ ); if ( !is_set_ )
{
is_set_ = true;
pthread_cond_signal ( &event_ );
} pthread_mutex_unlock ( &mutex_ );
} void Event::Wait ( )
{
pthread_mutex_lock ( &mutex_ ); while ( !is_set_ )
{
pthread_cond_wait ( &event_, &mutex_ );
} is_set_ = false;
pthread_mutex_unlock ( &mutex_ );
} LONG SyncInterlockedIncrement ( LONG *Dest )
{
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
return __sync_add_and_fetch ( Dest, 1 );
#else
register int result;
__asm__ __volatile__ ( "lock; xadd %0,%1" : "=r"( result ), "=m"( *Dest )
: "0"( 1 ), "m"( *Dest ) : "memory" );
return result;
#endif
} LONG SyncInterlockedExchangeAdd ( LONG *Dest, LONG Val )
{
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
return __sync_add_and_fetch ( Dest, Val );
#else
register int result;
__asm__ __volatile__ ( "lock; xadd %0,%1" : "=r"( result ), "=m"( *Dest )
: "0"( Val ), "m"( *Dest ) : "memory" );
return result;
#endif
} LONG SyncInterlockedExchange ( LONG *Dest, LONG Val )
{
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
return __sync_lock_test_and_set ( Dest, Val );
#else
register int result;
__asm__ __volatile__ ( "lock; xchg %0,%1" : "=r"( result ), "=m"( *Dest )
: "0"( Val ), "m"( *Dest ) : "memory" );
return result;
#endif
} #endif } // namespace Common
在界面线程不能使用Sleep和WaitForSingleObject之类的函数, 使用 MsgWaitForMultipleObjects

http://www.cnblogs.com/shangdawei/p/4042108.html

上一篇:SQL Server int类型值最大2147483647(2^31 - 1)


下一篇:6.类似Object监视器方法的Condition接口