描述线程之前先说一下win下的县城同步机制,分别为:
临界区 CCriticalSection 表示一个临界区,他是一个同步对象,同一时刻只允许一个线程存取资源。
互斥量 CMutex 一个同步对象,允许某线程共同访问同一个资源,区别于CCriticalSection使用CMutex可以保持进程间的同步。
事件 CEvent 允许一个时间发生时线程通知另外一个线程的同步对象。使用CEvent可以保持进程间的同步。
信号灯 CSemaphore 允许有限数目的线程在一个或多个进程中访问同一个资源。一个CSemaphore对象保持了对当前访问某一指定资源的线程的技术。使用CSemaphore可以保持进程间的同步
在这之前首先认识下函数WaitForSingleObject和WaitForMultipleObjects用来检测hHandle事件的信号状态。
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds)
hHandle 对象句柄,可指定一系列的对象,如Event、内存资源通知、互斥量、进程、信号灯、线程、可等待定时器等。
dwMilliseconds 超时时间毫秒级
DWORD WaitForMultipleObjects(
DWORD dwCount,
CONST HANDLE* phObjects,
BOOL fWaitAll,
DWORD dwMilliseconds)
dwCount用于指明想要让函数查看的内核对象的数量,这个值必须在1到MAXIMUM_WAIT_OBJECTS(64)之间。
phObjects指向内核对象句柄的数组指针。
fWaitAll用于指定函数的等待模式,如果为TRUE则函数等待直到所有对象都变为已通知状态或超时;如果为FALSE则函数直到对象中任意一个变为已通知状态或超时。
dwMilliseconds 同上WaitForSingleObject
CCriticalSection:无默认参数传递,也是唯一一个仅对线程负责的同步类。
CMutex:只允许一个线程占有某个资源。
CMutex(BOOL bInitiallyOwn = FALSE,
LPCTSTR lpszName = NULL,
LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);
bInitiallyOwn:用来指定互斥体对象初始状态是锁定(TRUE),还是非锁定(FALSE)
lpszName:互斥体名称
lpsaAttribute:安全属性事件对象
CEvent:有两种状态状态:有信号状态和无信号状态,线程监视位于其中的CEvent类对象的状态,在MFC中CEvent类对象有两种类型:人工事件和自动事件,一个自动CEvent对象再被至少一个线程释放后会自动返回到无信号状态;而人工事件对获得信号,可利用线程释放,但直到成员函数ReSetEvent才将其设置为无信号状态。
CEvent(BOOL bInitiallyOwn=FLASE,
BOOL bManualReset=FLASE,
LPCTSTR lpszName=NULL,
LPSECURITY_ATTRIBUTES lpsaAttribute=NULL)
bInitiallyOwn指定事件对象初始化状态,TRUE为有信号,FALSE为无信号;
bManualReset指定要创建的事件是属于人工事件还是自动事件。TRUE为人工事件,FALSE为自动事件;
lpszName事件的名称跨进程时需要名称,其他时候可以为NULL;
lpsaAttribute安全属性事件对象;
BOOL CEvent::SetEvent()
将CEvent类对象设置为有信号状态,如果是人工事件,CEvent类对象保持为有信号状态,直到调用成员函数ResetEvent()将其重新设为无信号状态时为止;如果为自动事件,则由系统自动重置为无信号。
BOOL CEvent::ResetEvent()
将函数对象的状态设置为无信号状态。
CSemaphore:
CSemaphore (LONG lInitialCount=1,
LONG lMaxCount=1,
LPCTSTR pstrName=NULL,
LPSECURITY_ATTRIBUTES lpsaAttributes=NULL)
lInitialCount信号量对象的初始计数值,即可访问线程数目的初值;
lMaxCount信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目;
使用CSemaphore类的构造函数创建信号量对象时要同时指出允许的最大资源计数和当前可用资源计数。
以上4个同步机制还带有专门配合使用的类CSingleLock和CMultiLock:
值得一提的是以上四个类都同时继承了CSyncObject
CSingleLock(CSyncObject* pObject,
BOOL bInitialLock = FALSE)
pObject同步成员指针
bInitialLock初始化时是否加锁,默认不加锁
CMultiLock(CSyncObject* ppObjects[],
DWORD dwCount,
BOOL bInitialLock = FALSE);
ppObjects同步成员数组指针
dwCount同步成员的个数
bInitialLock初始化时是否加锁,默认不加锁
下面是vc中线程的详细介绍
在Win32API中使用函数CreateThread
HANDLE CreateThread(LPSRCURITY_ATTRIBUTES lpThreadAttributes,
DOWRD dwStackSize,
LPTHREAD_START_POUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId)
lpThreadAttributes指向一个SECURITY_ATTRIBUTES结构的指针,该结构决定了线程的安全属性,一般为NULL
typedef struct_SECURITY_ATTRIBUTES{
DWORD nLenght;//结构体大小
LPVOID lpSecurityDescriptor;//对结构SECURITY_DESCRIPTOR指针控制对象的访问。
BOOL bInheriHandle;//安全描述符的对象能否被新创建的进程继承
}
dwStackSize线程栈的大小,如果为0使用默认大小1M
lpStartAddress线程函数的函数指针
lpParameter传入线程的函数
dwCreationFlags控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用
lpThreadId返回的线程ID
如果创建成功则返回线程的句柄,否则为空
DWORD SuspendThread(HANDLE hThread)
该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止
DWOED ResumeThread(HANDLE hThread)
该函数用于结束线程的挂起状态,恢复指定的线程。
void ExitThread(DWORD dwExitCode)
该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。
BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode)
应用程序可以调用TerminateThread强制终止某一线程的执行,但不释放线程所占用的资源,一般不建议使用该函数。
BOOL PostThreadMessage(DWORD idThread,
UINT Msg,
WPARAM wParam,
LPARAM lParam)
该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时返回
MFC中的多线程
AfxBeginThread分为两种模式工作者线程和用户界面线程,两者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环
工作者线程:
CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
nPriority=THREAD_PRIORITY_NORMAL,
UINT nStackSize=0,
DWORD dwCreateFlags=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
nPriority 如果为0继承父线程的优先级
nStackSize如果为0继承父线程栈空间大小
dwCreateFlags如果为0,则线程在创建后自可开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起。
用户界面线程
CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,
int nPriority=THREAD_PRIORITY_NORMAL,
UINT nStackSize=0,
DWORD dwCreateFlags=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
void AFXAPI AfxEndThread(UINT nExitCode,
BOOL bDelete = TRUE)
AfxEndThread必须在线程内部执行,如果在其他线程中结束该线程,必须采用线程通信的方法。
这里不详细描述了
uintptr_t _beginthread(
void( *start_address )( void * ),
unsigned stack_size,
void *arglist)
start_address线程函数指针
stack_size栈空间大小
arglist传递给线程的参数列表
unsigned long _beginthreadex(
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr )
stack_size栈空间大小
start_address线程函数指针
arglist传递给线程的参数指针
initflag线程初始状态0立即运行
_endthread、_endthreadex线程返回时会自动调用。
要注意的一点是:
CreateThread和_beginthreadex结束后需要调用CloseHandle关闭句柄,而_beginthread和AfxBeginThread则不需要调用CloseHandle。