1.基本函数
CreatThread()
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORDdw CreationFlags,
LPDWORD pThreadId
);
函数说明:
- 参数1,线程内核对象的安全属性,一般传入NULL表示使用默认设置
- 参数2,线程栈空间大小。传入0表示使用默认大小(1MB)
- 参数3,新线程所执行的线程函数地址,多个线程可以使用同一个函数地址
- 参数4,传给线程函数的参数,无参数时为NULL
- 参数5,控制线程的标志,0表示线程创建之后立即就可以进行调度;CREATE_SUSPENDED表示创建挂起的线程,暂停执行,此时线程无法调度,直到调用ResumeThread()
- 参数6,返回线程的ID,不需要返回则为NULL
- 返回值,成功则返回线程的句柄,失败返回NULL
C标准库没考虑多线程运行的情况,因此多线程调用C标准库时可能造成数据被覆盖篡改的情况。为避免这个问题,可以使用_beginthreadex()
函数来创建线程,生成每个线程都将拥有自己专用的一块内存区域来供标准C运行库中所有有需要的函数使用
WaitForSingleObject()
DWORD WINAPI WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
函数说明:等待对象被触发
- 参数1,对象句柄,可指定一系列对象
- 参数2,最长等待时间,以毫秒为单位,0为立即返回,INFINITE表示无限等待
- 返回值,WAIT_OBJECT_0,指定的时间内对象被触发;WAIT_TIMEOUT,超过最长等待时间对象仍未被触发返回;WAIT_FAILED传入参数有错误
线程的句柄在线程运行时是未触发的,线程结束运行,句柄处于触发状态。所以可以WaitForSingleObject()来等待一个线程结束运行
- 内核对象被占用,处于未触发,无信号状态
- 内核对象未被占用时,处于触发,有信号状态
WaitForMultipleObjects()
DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);
函数说明:等待多个内核对象触发返回
- 参数1,句柄的数量 最大值为MAXIMUM_WAIT_OBJECTS(64)
- 参数2,句柄数组的指针, 类型可以为(Event,Mutex,Process,Thread,Semaphore )数组
- 参数3,等待的类型,如果为TRUE 则等待所有信号量有效再返回,FALSE 当有其中一个信号量有效时就返回
- 参数4,INFINITE为无限等待直到所有对象触发,0为立即返回,继续向下执行
- 返回值,WAIT_OBJECT_0到WAIT_OBJECT_0 + nCount - 1,如果bWaitAll为TRUE,则返回值表明所有指定对象的状态信号。如果bWaitAll为FALSE,其返回值减去WAIT_OBJECT_0 就是参数lpHandles数组的序号。如果同时有多个内核对象被触发,这个函数返回的只是其中序号最小的那个
2.关键段
CRITICAL_SECTION
- 初始化函数
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
- 销毁函数
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
- 进入关键段
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
- 离开关键段
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
关键段会记录拥有该关键段的线程句柄即关键段是有“线程所有权”,拥有关键段的线程可多次进入关键段,相关参数会记录进入关键段的次数,当该参数变成0时,系统会更新关键段,将等待中的线程变为可调度状态
3.事件Event
创建事件CreateEvent
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOLbManua lReset,
BOOL bInitialState,
LPCTSTR lpName
);
- 参数1,安全控制参数,一般直接传入NULL
- 参数2,置位参数,TRUE表示手动置位,FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。
- 参数3,事件的初始状态,TRUE表示已触发,FALSE表示未触发
- 参数4,事件的名称,NULL表示匿名事件
根据事件名获取事件句柄OpenEvent
HANDLE OpenEvent(
DWORDdwDesiredAccess,
BOOLbInheritHandle,
LPCTSTRlpName //名称
);
- 参数1,访问权限,对事件一般传入EVENT_ALL_ACCESS
- 参数2,事件句柄继承性,一般传入TRUE即可
- 参数3,名称,不同进程中的各线程可以通过名称来确保它们访问同一个事件
触发事件SetEvent
BOOL SetEvent(HANDLE hEvent);
设置事件为可触发状态(未被占用、有信号)
和WaitSingleObject配合使用,事件自动置位(FALSE)时,接收到SetEvent的信号后,系统自动将事件重置为不可触发状态;而事件手动置位(TRUE)时,需要手动调用ResetEvent重置事件的状态
设置事件未触发ResetEvent
BOOL ResetEvent(HANDLE hEvent);
事件的清理和销毁使用CloseHandle()
- 事件是内核对象,事件分为手动置位事件和自动置位事件。事件Event内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发
- 事件可以由SetEvent()来触发,由ResetEvent()来设成未触发。还可以由PulseEvent()来发出一个事件脉冲
- 事件可以解决线程间同步问题,因此也能解决互斥问题
4.互斥量Mutex
创建互斥量CreateMutex
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
- 参数1,安全控制,一般直接传入NULL
- 参数2,确定互斥量的初始拥有者,TRUE表示由创建它的线程拥有,此时处于未触发状态;FALSE表示互斥量不被任何线程占用,处于触发状态
- 参数3,设置互斥量的名称,可以为NULL
打开互斥量OpenMutex
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName //名称
);
- 参数1,访问权限,对互斥量一般传入MUTEX_ALL_ACCESS
- 参数2,互斥量句柄继承性,一般传入TRUE
- 参数3,互斥量名称。某一个进程中的线程创建互斥量后,其它进程中的线程就可以通过名称找到这个互斥量
释放互斥量ReleaseMutex
BOOL ReleaseMutex (HANDLE hMutex)
访问结束后,线程调用释放函数,其他线程可以访问互斥量
互斥量是内核对象,清理互斥量使用CloseHandle()
- 互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步
- 互斥量能够用于多个进程之间线程互斥问题,当拥有互斥量的线程意外终止时,互斥量会被系统回收,确保其他线程可以继续使用
4.信号量Semaphore
创建信号量CreateSemaphore
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
- 参数1,安全控制,一般为NULL
- 参数2,初始资源数量
- 参数3,最大并发数量
- 参数4,信号量名称,NULL表示匿名信号量
打开信号量OpenSemaphore
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
- 参数1,访问权限,对一般传入SEMAPHORE_ALL_ACCESS
- 参数2,信号量句柄继承性,一般传入TRUE
- 参数3,信号量名称
增加信号量资源计数ReleaseSemaphore
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
- 参数1,信号量句柄
- 参数2,资源增加个数,大于0且小于最大资源数
- 参数3,传出增加前的资源计数,NULL为不用传出
- 信号量是内核对象,使用CloseHandle()清理和销毁
- 当前资源数量大于0,表示信号量处于触发,等于0表示资源已经耗尽故信号量处于末触发