一、创建线程的四种方法
1.1 C++11 std:thread
1.2 windows api _beginthreadex/_endthreadex
uintptr_t _beginthreadex( void *security, //线程安全属性。
unsigned stack_size, //线程堆栈大小,可以为0。
unsigned ( *start_address )( void * ),//线程函数的入口地址。对于_beginthread,线程函数调用约定是_cdecl。对于_beginthreadex,线程函数调用约定是_stdcall。
void *arglist, //传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针,可以为NULL。
unsigned initflag, //线程创建的初始标志。为CREATE_SUSPENDED则挂起线程,使用ResumeThread激活线程,为0则立即执行。
unsigned *thrdaddr //线程Id。
);
-
_beginthreadex不会自动关闭线程句柄,需要使用CloseHandle现实的关闭句柄。所以_beginthreadex函数可以用WaitForSingleObject() 函数来获取线程对象来进行同步。
-
_endthreadex显示式结束一个线程,线程函数返回时也会自动调用_endthreadex
1.3 windows api CreateThread/ExitThread
HANDLE WINAPI CreateThread(
_in LPSECURITY_ATTRIBUTES lpThreadAttributes,//指向一个LPSECURITY_ATTRIBUTES结构的指针决定返回的句柄能否被继承,lpThreadAttributes空,不能被继承。
_in SIZE_T dwStackSize, //初始化的堆栈大小,以字节为单位。如果为0,使用默认的大小(1MB)。
_in LPTHREAD_START_ROUTINE lpStartAddress, //函数的入口地址,是一个函数指针。 函数原型:DWORD WINAPI ThreadProc( [in] LPVOID lpParameter);
_in LPVOID lpParameter, //一个指针,被传递到线程函数里。
_in DWORD dwCreationFlags, //线程创建的标志。如果为CREATE_SUSPENDED,那么需要使用ResumeThread函数来激活线程函数,如果为0,线程函数立刻执行。
_out LPDWORD lpThreadId //一个指向线程id的指针,如果为空,线程id不被返回。
);
-
如果线程函数return,返回值会隐式调用ExitThread函数,可以使用GetExitCodeThread函数获得该线程函数的返回值
-
如果一个线程调用了CRT,应该使用_beginthreadex 和_endthreadex(需要使用多线程版的CRT)
1.4 MFC AfxBeginThread创建线程
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass, //线程函数的入口地址。函数原型:UINT __cdecl MyControllingFunction( LPVOID pParam );
int nPriority = THREAD_PRIORITY_NORMAL, //线程优先级。
UINT nStackSize = 0, //指明线程堆栈的大小,以字节为单位,可以为0。
DWORD dwCreateFlags = 0, //线程创建标志。
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL //线程安全属性。
);
- 可以调用AfxEndThread来终止线程或者return
二、退出线程方法
- 线程函数的return返回(最好),会自动调用退出线程函数
- 调用 _endthreadex()函数 或 ExitThread()函数(最好不要):
- 如果使用这两种方法退出线程, 则不会执行线程函数的return语句, 所以就不会调用线程函数作用域内申请的类对象的析构函数, 会造成内存泄露.
- 线程调用了一个子函数,如果子函数决定退出线程,return是没用的,_endthreadex即可终结线程
- std:thread创建的线程,只能自己实现退出线程逻辑;thread::detach()函数被调用后,执行的线程从线程对象中被分离,线程会继续执行。
三、线程同步方法
2.1. WaitForSingleObject函数
DWORD WINAPI WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
函数功能: 等待函数 – 使线程进入等待状态,直到指定的内核对象被触发(等待CreateEvent有信号或线程结束被触发)
参数说明:
- 第一个参数: 为要等待的内核对象(可以是线程句柄/CreateEvent)。
- 第二个参数: 为最长等待的时间,以毫秒为单位,如传入5000就表示5秒,传入0就立即返回,传入INFINITE表示无限等待
- 函数返回值: 在指定的时间内对象被触发,函数返回WAIT_OBJECT_0。超过最长等待时间对象仍未被触发返回WAIT_TIMEOUT。传入参数有错误将返回WAIT_FAILED
2.2. CreateEvent()
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
BOOL bManualReset, // reset type
BOOL bInitialState, // initial state
LPCTSTR lpName // object name
);
函数功能: 创建一个Event同步对象,假设CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL
参数说明:
- lpEventAttributes 一般为NULL
- bManualReset:
- 假设true,人工复位,一旦该Event被设置为有信号,则它一直会等到ResetEvent()API被调用时才会恢复 为无信号。
- 假设为false,主动复位,Event被设置为有信号,则当有一个wait到它的Thread时, 该Event就会自己主动复位,变成无信号;
- bInitialState 初始状态,true,有信号,false无信号
- lpName 事件对象的名称,可以为NULL
关联函数:
- 一个Event被创建以后,能够用OpenEvent()API来获得它的Handle
- CloseHandle() 来关闭它
- SetEvent()或PulseEvent()来设置它使其有信号
- ResetEvent() 来使其无信号
- WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号.
2.3. CreateMutex()创建互斥对象
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性指针
BOOL bInitialOwner, // 初始拥有者
LPCTSTR lpName // 互斥对象名
);
- 函数功能: 创建互斥对象
- 参数说明:
- lpMutexAttributes一般位NULL
- bInitialOwner主要用来控制互斥对象的初始状态。一般多将其设置为FALSE,以表明互斥对象在创建时并没有为任何线程所占有
- 创建互斥对象时指定了对象名,那么可以在本进程其他地方或是在其他进程通过OpenMutex()函数得到此互斥对象的句柄
- 关联函数:
- ReleaseMutex()释放其拥有的互斥对象
- WaitForSingleObject函数等待互斥对象通知(Release后可能通知到),返回值位WAIT_ABANDONED_0
2.4. 临界区操作线程同步
>临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。 - 使用步骤:
- CRITICAL_SECTION g_cs; //临界区结构对象
- InitializeCriticalSection(&g_cs); // 初始化临界区
- EnterCriticalSection(&g_cs); // 进入临界区
- LeaveCriticalSection(&g_cs); //离开临界区
2.5. CreateSemaphore()创建信号量
信号量(Semaphore)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针
LONG lInitialCount, // 初始计数
LONG lMaximumCount, // 最大计数
LPCTSTR lpName // 对象名指针
);
- 使用步骤:
- hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);// 创建信号量对象
- WaitForSingleObject(hSemaphore, INFINITE);// 试图进入信号量关口
- ReleaseSemaphore(hSemaphore, 1, NULL);// 释放信号量计数