Windows下的进程和Linux下的进程是不一样的,它比较懒惰,从来不执行任何东西,它只是为线程提供执行环境,然后由线程负责执行包含在进程的地址空间中的代码。当创建一个进程的时候,操作系统会自动创建这个进程的第一个线程,成为主线程。线程由两部分组成:一是线程的内核对象。操作系统用它来对线程实施管理,内核对象也是系统用来存放线程统计信息的地方。二是线程栈。线程栈用于维护线程在执行代码时所需的所有函数参数和局部变量。线程可以访问所在进程的内核对象的所有句柄、所有内存和这个进程中的其他线程的堆栈。

    在Windows下你可以调用Win SDK的API来创建一个线程,也可以用使用C Run-Time Library中的函数来创建,还可以使用MFC封装的相关线程函数来得到你想要的线程。最近正好阅读了相关方面的书籍,正好做下总结。

一  WinAPI下的线程

1 创建线程

HANDLE
WINAPI
CreateThread(
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId
);
  • lpThreadAttributes

  指向LPSECURITY_ATTRIBUTES结构体的指针,默认设为NULL,让线程使用默认的安全性。如果希望所有子线程能够继承线程对象的句柄,则必须设定LPSECURITY_ATTRIBUTES结构体,将它的bInHeriHandle初始化未TRUE。

  • dwStackSize

    设置初始栈的大小,默认设为0。

  • lpStartAddress

  指向LPTHREAD_START_ROUTINE指向的函数指针,即为新线程的起始地址。该函数的名称任意,但是函数类型必须遵照下面声明的形式:

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
LPVOID lpThreadParameter
);
  • lpParameter

  新线程调用函数的命令行参数。

  • dwCreationFlags

  用于控制线程创建的附加标记。可以使两个值之一:CRETE_SUSPENDED或0。如果是SUSPENDED,那么线程创建后将处于暂停状态。如果是0,线程在创建后立即运行。

  • lpThreadId

  指向一个变量,用来接受线程的ID。如果对线程ID不感兴趣可以直接设为NULL。

  注意:CreateThread()函数传回的值有两个,第一个值是返回值HANDLE,这个值是线程的HANDLE,是线程所在的进程中的局部变量,在不同的进程中是不唯一的,所以你不能直接跨进程的把一个线程的HANDLE传给另外一个进程中的线程让其使用。第二个值是由lpThreadId传回的线程ID,此值是一个全局变量,可以独一无二的表示系统中任意进程中的某个线程。比如调用AttachThreadInput()或PostThreadMessage()就需要用线程ID,而不能用线程的HANDLE。

  2  关闭线程句柄

    当完成工作后,应该带哦用CloseHandle释放核心对象。

BOOL
WINAPI
CloseHandle(
__in HANDLE hObject
);

  如果成功则传回TRUE,失败则传回FALSE。当创建一个线程后立刻调用CloseHandle()函数并不会终止新创建的线程,只是表示在主线程中新创建的线程的引用不感兴趣,因此将它关闭。当关闭该线程的句柄时,系统会递减该线程内核对象的使用计数。线程被创建的时候,线程内核对象的默认引用计数是2,当创建的这个新线程执行完毕后,系统也会递减该线程内核对象的引用计数。当使用计数为0时,系统会释放该线程内核对象。如果没有关闭线程句柄,系统就会一直保持对线程内核对象的引用,这样即使线程执行完毕,它的引用计数仍然不会为哦,这样该线程内核对象也就不会被释放,只有等到进程终止时,系统才会清理这些残留的对象。因此在程序中,当不再需要线程的句柄时,应将其关闭。

3 线程结束代码

BOOL
WINAPI
GetExitCodeThread(
__in HANDLE hThread,
__out LPDWORD lpExitCode
);

  如果成功,GetExitCodeThread()传回TRUE,否则传回FALSE。GetExitCodeThread()将传回线程函数的返回值,但是当线程还在进行尚未结束代码时,它会传回TRUE表示成功,此时lpExitCode指向的内存区域存放的是STILL_ACTIVE。

  4 结束线程

  当线程函数结束的时候,线程也就结束了,但是还可以用更暴力的手段让线程结束,就是调用线程结束函数ExitThread():

VOID
WINAPI
ExitThread(
__in DWORD dwExitCode
);

此函数可以在任何时候被调用而绝不会返回,任何在它之后的代码,都不会被调用。

还可以调用函数TerminateThread函数来杀死一个线程,不同于ExitThread总是杀死主调线程,TerminateThread可以杀死任何线程。

VOID
WINAPI
ExitThread(
HANDLE hThread,
DWORD dwExitCode
);

示例程序1:创建5个线程,此5个线程的调用函数打印出当前线程正在运行,然后返回。同时,我们另外创立一个监控监控线程,来监控这个5个线程的状态。

#include <windows.h>
#include <process.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <tchar.h> #define THREAD_NUM 5 DWORD WINAPI ThreadFunc(LPVOID);
DWORD WINAPI MonitorFunc(LPVOID); void ExitFunc(void); int main( )
{
HANDLE hThread[THREAD_NUM]; for (int i = ; i < THREAD_NUM; ++i)
{
hThread[i] = CreateThread(NULL, , ThreadFunc,(LPVOID)i, , NULL);
} HANDLE monitorThread;
monitorThread = CreateThread(NULL, , MonitorFunc, (LPVOID)hThread, , NULL);
if (WaitForSingleObject(monitorThread, INFINITE))
CloseHandle(monitorThread); for (int i = ; i < THREAD_NUM; ++i)
{
if (WaitForSingleObject(hThread[i], INFINITE))
{
CloseHandle(hThread[i]);
}
} cout<<"所有线程已经运行结束!"<<endl; system("pause"); return EXIT_SUCCESS; } DWORD WINAPI ThreadFunc(LPVOID lpPara)
{
cout<<"线程"<< (int)(lpPara)<<"正在运行"<<endl;
Sleep();
return ;
} DWORD WINAPI MonitorFunc(LPVOID hThread)
{
DWORD exitCoed;
int exitThreadCount = ;
int threadStatus[THREAD_NUM];
memset(threadStatus, , THREAD_NUM); while(TRUE)
{
for (int i = ; i < THREAD_NUM; ++i)
{
GetExitCodeThread( ((HANDLE*)hThread)[i], &exitCoed);
if (exitCoed == STILL_ACTIVE)
cout<<"根据ExitCode判断:线程"<<i<<"仍然在运行!"<<endl;
else
{
cout<<"根据ExitCode判断:线程"<<i<<"已经结束运行"<<endl;
threadStatus[i] = ;
}
} exitThreadCount = ;
for (int i = ; i < THREAD_NUM; ++i)
exitThreadCount += threadStatus[i]; if (exitThreadCount == THREAD_NUM)
break;
} return ; }

二  C Run-time Library 下的线程

   在《Win32 多线程程序设计》一书中说道:“在微软的Programing Techniques说明文件中有一句看似悲惨的警告”:

  警告:如果你在一个与LIBCMT.LIB链接的程序中调用C runtime函数,你的线程就必须以_beginthread()启动之,不要使用Win32的ExitThread()和ExitThread()。这

  这句警告是说_beginthread有一个冲突状态,不可完全信赖它。所以在C Runtime Library中应该用_beiginthreadex()和_endthreadex()。主要原因是C Runtime libray是上个世纪70年代产生出来的,那个时候多任务还是新奇概念,所以更不知道多线程是什么东东了。这导致C Runtime libray中的使用的数个全局变量和静态变量可能在多线程程序中彼此引起冲突,比如errno。还有一点,_beginthread产生出来的线程所做的第一件事情就是关闭自己的handle。所以现在Visual C++提供了两个版本的C Runtime libray,一个版本供单线程程序使用,一个供多线程程序使用。第三部分介绍的MFC程序必须使用多线程的C Runtime libray,否则在链接时会出现错误。

  1 线程的创建

uintptr_t __cdecl _beginthreadex(_In_opt_ void * _Security,
_In_ unsigned _StackSize,
_In_ unsigned (__stdcall * _StartAddress) (void *),
_In_opt_ void * _ArgList,
_In_ unsigned _InitFlag,
_In_opt_ unsigned * _ThrdAddr);

  参数说明:

  • _Security 

  相当于CreateThread()中的安全属性参数,默认设置为NULL。对应Win32数据类型是LPSECURITY_ATTRIBUTES。

  • _StackSize

新线程的堆栈大小,单位是字节(byte)。对应Win32数据类型是DWORD

  • _StartAddress

  线程启动函数。

  • _ArgList

  新线程将接收到一个指针,这个指针指示单纯的被传过去,运行库并没有对它做拷贝操作。

  • _InitFlag

  启动时的状态标记。

  • _ThrdAddr

新线程的ID通过此参数传回。

_beginthreadex函数的返回值为handle,这个值必须被强制转换为Win32的HANDLE之后才能被使用。如果函数失败,则传回0,而其原因被设置在errno和doserrno全局变量中。

2 关闭线程句柄

  在C Runtime Libray下的_beiginthreadex()创建线程内部调用的就是WinAPI的CreateThread()函数。这也是很让人蛋疼的地方,微软想让C Runtime Libray跨平台,但是却使用的WinAPI的函数,想法是好的,只是没有去实现。正是这样子,所以_beiginthreadex()的返回值handle就是WinAPI中的HANDLE,只是需要强制转换下才能使用。所以,在C Runtime Libray下关闭一个线程的句柄同样是调用函数CloseHandle()。

3 结束一个线程

  在C Runtime Libray下于ExitThread()相对应的函数是_endthreadex():

 void __cdecl _endthreadex(_In_ unsigned _Retval);

  和ExitThread()函数一样,_endthreadex()可以被线程在任意时间使用,它需要表示一个“线程返回代码”的参数。但是绝对不要再一个以_beiginthreadex()启动的线程中调用ExitThrad(),因为这样C Runtime Libray就没有机会释放该线程的资源了。

  为了适当的清除C Runtime Libray中的结构,对于以_beiginthreadex()来产生新线程的程序,应该使用下面两种方法来结束程序:

  1. 调用C Runtime Libray中的exit()函数。
  2. 从main()返回系统。

  任何一种情况下,runtime libray都会自动进行清理操作,组后调用ExitProcess()。使用任一种技术都不会等待线程的结束,。

三 MFC中的线程

  如果要在MFC程序中产生一个线程,而该线程将调用MFC函数或使用MFC的任何数据,那么必须以AfxBeginThread()或CWinThread::CreateThrad()来产生这些线程。 

Windows下的多线程的更多相关文章

  1. C#Stimulator项目>>>C/C++ DLL的生成和调用,Windows下的多线程

    Windows下的多线程 http://blog.csdn.net/ganpengjin1/article/category/2541791 使用C/C++建立DLL,环境VS2013 新建Win32 ...

  2. Windows下PHP多线程扩展pthreads的安装

    pthreads扩展安装步骤 1.查看phpinfo() 获取PHP版本号及位数(x86表示32位,x64表示64位).编译器版本.PHP配置文件加载所在位置等.如下图所示: 2.pthreads扩展 ...

  3. Windows下C++多线程同步与互斥简单运用

    1.  互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...

  4. Windows下C++多线程同步与互斥简单运用(转)

    1.  互斥量,Mutex #include <Windows.h> #include <iostream> using namespace std; DWORD WINAPI ...

  5. Windows下多线程编程(一)

    前言 熟练掌握Windows下的多线程编程,能够让我们编写出更规范多线程代码,避免不要的异常.Windows下的多线程编程非常复杂,但是了解一些常用的特性,已经能够满足我们普通多线程对性能及其他要求. ...

  6. Windows平台下的多线程编程

    线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件.信号标识及动态分配的内存等.一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度 ...

  7. 在Windows下使用Dev-C++开发基于pthread.h的多线程程序【转】

    在Windows下使用Dev-C++开发基于pthread.h的多线程程序[转]     在Windows下使用Dev-C++开发基于pthread.h的多线程程序   文章分类:C++编程     ...

  8. 原创 C++应用程序在Windows下的编译、链接:第一部分 概述

    本文是对C++应用程序在Windows下的编译.链接的深入理解和分析,文章的目录如下: 我们先看第一章概述部分. 1概述 1.1编译工具简介 cl.exe是windows平台下的编译器,link.ex ...

  9. C#实现http协议下的多线程文件传输

    用C#实现HTTP协议下的多线程文件传输转自  http://developer.51cto.com/art/201105/263066_all.htm C#(C Sharp)是微软(Microsof ...

随机推荐

  1. ♫【CSS】命名颜色

    color: aqua; 每个浏览器都支持一套它能识别且能正确渲染的命名颜色.这些颜色包括: aqua 浅绿色 #00ffffblack 黑色 #000000blue 蓝色 #0000fffuchsi ...

  2. Android Loader详解三:重启与回调

    重启装载器 当你使用initLoader()时,如果指定ID的装载器已经存在,则它使用这个装载器.如果不存在呢,它将创建一个新的.但是有时你却是想丢弃旧的然后开始新的数据. 要想丢弃旧数据,你应使用r ...

  3. HDU-1518 Square(DFS)

    Square Time Limit : 10000/5000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submi ...

  4. 发送带有认证信息的HTTP请求并取回响应

    问题 如何发送 一个带有网络认证证书的HTTP请求并返回相应的HTTP响应. 设计 创建一个WebRequest对象和一个NetWorkCredential对象.把NetWorkCredential对 ...

  5. Linux Shell脚本中点号和source命令

    Linux中一个文件是根据其是否具有执行属性来判断他是否可以直接运行的.就像Windows下的exe一样.如果我们要执行某一个文件,可以先将其权限修改为可执行(必须是所有者或者root才能修改).然后 ...

  6. 特性扩展:WebActivatorEx

    WebActivator: PreApplicationStartMethod 属性,用于标记作为东西您获取 Web 应用程序启动时调用的方法,在Application_Start前. PostApp ...

  7. HW4.22

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  8. linux内核数据结构--进程相关

    linux里面,有一个结构体task_struct,也叫“进程描述符”的数据结构,它包含了与进程相关的所有信息,它非常复杂,每一个字段都可能与一个功能相关,所以大部分细节不在我的研究范围之内,在这篇文 ...

  9. PAT 1080. Graduate Admission (30)

    It is said that in 2013, there were about 100 graduate schools ready to proceed over 40,000 applicat ...

  10. hadoop2.2.0集群安装

    位说明. 位).Jdk使用的1.7(1.6也可以).网络配置好,相互可以ping通,java环境安装完毕.   第一部分 Hadoop 2.2 下载 位). 下载地址:http://apache.cl ...