当进程结束后所有线程都终止。多线程编程最大的问题在于共享数据的访问控制。
直接用Win32 API进行编程有很多优点:基于Win32的应用程序执行代码小,运行效率高。但是它要求程序员编写的代码较多,且需要管理所有系统提供给程序的资源。▶ 创建线程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD SIZE_T dwStackSize, // initial stack size LPTHREAD_START_ROUTINE lpStartAddress, // thread function LPVOID lpParameter, // thread argument DWORD dwCreationFlags, // creation option LPDWORD lpThreadId // thread identifier ); 线程函数原型:DWORD WINAPI Fun(LPVOID lpParamter); 参数说明:lpThreadAttributes :NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE。dwStackSize:设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。lpStartAddress:指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:DWORD WINAPI ThreadProc (LPVOID lpParam) ,格式不正确将无法调用成功。但lpStartAddress要这样通过LPTHREAD_START_ROUTINE转换如:(LPTHREAD_START_ROUTINE)MyVoid。MyVoid声明为 void MyVoid();lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。dwCreationFlags:可取如下标志1. CREATE_SUSPENDED(0x00000004):创建一个挂起的线程2. 0:表示创建后立即激活3. STACK_SIZE_PARAM_IS_A_RESERVATION(0x00010000):dwStackSize参数指定初始的保留堆栈的大小,否则,dwStackSize指定提交的大小。该标记值在Windows 2000/NT and Windows Me/98/95上不支持。lpThreadId:保存新线程的id。返回值:如果CreateThread成功,传回一个handle,代表新线程。否则传回一个FALSE。如果失败,你可以调用GetLastError()获知原因。必须指定的参数是lpStartAddress,其它的都可以用默认值0或NULL。
▶ 恢复/挂起线程
DWORD WINAPI ResumeThread( __in HANDLE hThread);
调用该函数可以激活一个挂起的线程。这个函数和SuspendThread相对应。创建线程是可以创建挂起的线程(dwCreationFlags参数指定),挂起的线程直到调用ResumeThread才开始执行。 设置线程优先级
BOOL SetThreadPriority( HANDLE hThread, // handle to the thread int nPriority // thread priority level); nPriority 优先级别参数 可设置为以下参数THREAD_PRIORITY_ABOVE_NORMAL 为比一般优先级高一个等级THREAD_PRIORITY_BELOW_NORMAL 比一般低一个等级THREAD_PRIORITY_HIGHEST 比一般高2个等级THREAD_PRIORITY_IDLETHREAD_PRIORITY_LOWEST 比一般低2个等级THREAD_PRIORITY_NORMAL 一般等级THREAD_PRIORITY_TIME_CRITICAL▶ 终止线程
线程内终止
如果某线程调用了ExitThread函数就可以终止它自己。VOID ExitThread(DWORD dwExitCode);dwExitCode: 指定线程的推出码。 线程外终止BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);作用: 在线程外终止一个线程,用于强制终止线程。 参数说明: HANDLE htread:被终止的线程的句柄,为CWinThread指针。 DWORD dwExitCode:退出码。▶ 关闭句柄
在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
但是这个handle并不能完全代表这个线程,它仅仅是线程的一个“标识”,系统和用户可以利用它对相应的线程进行必要的操纵。如果在线程成功创建后,不再需要用到这个句柄,就可以在创建成功后,线程退出前直接CloseHandle掉,但这并不会影响到线程的运行。不执行CloseHandle() 带来的后果:
若在线程执行完之后,没有通过CloseHandle()将引用计数减1,在进程执行期间,将会造成内核对象的泄露,相当与句柄泄露,但不同于内存泄露, 这势必会对系统的效率带来一定程度上的负面影响。但是,请记住,当进程结束退出后,系统仍然会自动帮你清理这些资源。但是在这里不推荐这种做法,毕竟不是 一个良好的编程习惯!( 应用程序运行时,有可能泄露内核对象,但是当进程终止运行时,系统能确保所有内容均被正确地清除。另外,这个情况是用于所有对象,资源和内存块,也就是说,当进程终止时,系统将保证不会留下任何对象。)▶ 判断程序运行状态
线程结束代码可以藉由调用GetExitCodeThread()而得知。BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode);▶ 同步与互斥
Win32 API提供了一组能是线程阻塞自身执行的等待函数。这些等待函数在其参数中的一个或多个同步对象产生了信号,或者超过规定的等待时间才会返回。在等待未返回时,线程处于等待状态,此时线程只消耗很少的CPU。
最常用的等待函数是:DWORD WINAPI WaitForSingleObject( __in HANDLE hHandle, __in DWORD dwMilliseconds); 参数:hHandle: 对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。当等待仍在挂起状态时,句柄被关闭,那么函数行为是未定义的。该句柄必须具有 SYNCHRONIZE 访问权限。dwMilliseconds: 定时时间间隔,单位为milliseconds(毫秒).如果指定一个非零值,函数处于等待状态直到hHandle 标记的对象被触发,或者时间到了。如果dwMilliseconds 为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。如果dwMilliseconds 为INFINITE,对象被触发信号后,函数才会返回。返回值:
WAIT_ABANDONED 0x00000080:当hHandle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值。 WAIT_OBJECT_ 0 0x00000000 :核心对象已被激活 WAIT_TIMEOUT 0x00000102:等待超时WAIT_FAILED 0xFFFFFFFF :出现错误,可通过GetLastError得到错误代码