windows 创建线程

1:  出自: http://blog.csdn.net/u012877472/article/details/49721653

CreateThread:


线程是进程中的一个实体,是被系统独立调度和分派的基本单位。一个进程可以拥有多个线程,但是一个线程必须有一个进程。线程自己不拥有系统资源,只有运行所必须的一些数据结构,但它可以与同属于一个进程的其它线程共享进程所拥有的全部资源,同一个进程中的多个线程可以并发执行。

在C/C++中可以通过CreateThread函数在进程中创建线程,函数的具体格式如下:

[cpp]  view plain  copy
  1. HANDLE CreateThread(  
  2.                     LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  3.                     DWORD dwStackSize,  
  4.                     LPTHREAD_START_ROUTINE lpStartAddress,  
  5.                     LPVOID lpParameter,  
  6.                     DWORD dwCreationFlags,  
  7.                     LPDWORD lpThreadID  
  8.                    );  
参数的含义如下:

lpThreadAttrivutes:指向SECURITY_ATTRIBUTES的指针,用于定义新线程的安全属性,一般设置成NULL;

dwStackSize:分配以字节数表示的线程堆栈的大小,默认值是0;

lpStartAddress:指向一个线程函数地址。每个线程都有自己的线程函数,线程函数是线程具体的执行代码;

lpParameter:传递给线程函数的参数;

dwCreationFlags:表示创建线程的运行状态,其中CREATE_SUSPEND表示挂起当前创建的线程,而0表示立即执行当前创建的进程;

lpThreadID:返回新创建的线程的ID编号;

如果函数调用成功,则返回新线程的句柄,调用WaitForSingleObject函数等待所创建线程的运行结束。函数的格式如下:

[cpp]  view plain  copy
  1. DWORD WaitForSingleObject(  
  2.                           HANDLE hHandle,  
  3.                           DWORD dwMilliseconds  
  4.                          );  
参数的含义如下:

hHandle:指定对象或时间的句柄;

dwMilliseconds:等待时间,以毫秒为单位,当超过等待时间时,此函数返回。如果参数设置为0,则该函数立即返回;如果设置成INFINITE,则该函数直到有信号才返回。

一般情况下需要创建多个线程来提高程序的执行效率,但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对一个内存地址进行写入操作,由于CPU时间调度的问题,写入的数据会被多次覆盖,所以要使线程同步。

就是说,当有一个线程对文件进行操作时,其它线程只能等待。可以通过临界区对象实现线程同步。临界区对象是定义在数据段中的一个CRITICAL_SECTION结构,Windows内部使用这个结构记录一些信息,确保同一时间只有一个线程访问改数据段中的数据。

使用临界区的步骤如下:

(1)初始化一个CRITICAL_SECTION结构;在使用临界区对象之前,需要定义全局CRITICAL_SECTION变量,在调用CreateThread函数前调用InitializeCriticalSection函数初始化临界区对象;

(2)申请进入一个临界区;在线程函数中要对保护的数据进行操作前,可以通过调用EnterCriticalSection函数申请进入临界区。由于同一时间内只能有一个线程进入临界区,所以在申请的时候如果有一个线程已经进入临界区,则该函数就会一直等到那个线程执行完临界区代码;

(3)离开临界区;当执行完临界区代码后,需要调用LeaveCriticalSection函数离开临界区;

(4)删除临界区;当不需要临界区时调用DeleteCriticalSection函数将临界区对象删除;

下面的代码创建了5个线程,每个线程在文件中写入10000个“hello”:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. HANDLE hFile;  
  4. CRITICAL_SECTION cs;//定义临界区全局变量  
  5. //线程函数:在文件中写入10000个hello  
  6. DWORD WINAPI Thread(LPVOID lpParam)  
  7. {  
  8.     int n = (int)lpParam;  
  9.     DWORD dwWrite;  
  10.     for (int i = 0;i < 10000;i++)  
  11.     {  
  12.         //进入临界区  
  13.         EnterCriticalSection(&cs);  
  14.         char data[512] = "hello\r\n";  
  15.         //写文件  
  16.         WriteFile(hFile, data, strlen(data), &dwWrite, NULL);  
  17.         //离开临界区  
  18.         LeaveCriticalSection(&cs);  
  19.     }  
  20.     printf("Thread #%d returned successfully\n", n);  
  21.     return 0;  
  22. }  
  23. int main()  
  24. {  
  25.     char *filename = "hack.txt";  
  26.     WCHAR name[20] = { 0 };  
  27.     MultiByteToWideChar(CP_ACP, 0, filename, strlen(filename) + 1, name, sizeof(name) / sizeof(name[0]));  
  28.     //创建文件  
  29.     hFile = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
  30.     if (hFile == INVALID_HANDLE_VALUE)  
  31.     {  
  32.         printf("CreateFile error.\n");  
  33.         return 0;  
  34.     }  
  35.     DWORD ThreadID;  
  36.     HANDLE hThread[5];  
  37.     //初始化临界区  
  38.     InitializeCriticalSection(&cs);  
  39.     for (int i = 0;i < 5;i++)  
  40.     {  
  41.         //创建线程,并调用Thread写文件  
  42.         hThread[i] = CreateThread(NULL, 0, Thread, (LPVOID)(i + 1), 0, &ThreadID);  
  43.         printf("Thread #%d has been created successfully.\n", i + 1);  
  44.     }  
  45.     //等待所有进程结束  
  46.     WaitForMultipleObjects(5, hThread, TRUE, INFINITE);  
  47.     //删除临界区  
  48.     DeleteCriticalSection(&cs);  
  49.     //关闭文件句柄  
  50.     CloseHandle(hFile);  
  51.     return 0;  
  52. }  
结果如图:





2:CreateThread, AfxBeginThread,_beginthread, _beginthreadex的区别

转载自:http://blog.csdn.net/qq_16149777/article/details/51910836

1、CreateThread——Windows的API函数
2、_beginthreadex——MS对C Runtime库的扩展SDK函数

3、AfxBeginThread——MFC中线程创建的MFC函数


【参考1】CreateThread, AfxBeginThread,_beginthread, _beginthreadex的区别 =====================================================================


1、CreateThread——Windows的API函数
2、_beginthreadex——MS对C Runtime库的扩展SDK函数
3、AfxBeginThread——MFC中线程创建的MFC函数


CreateThread
(API函数:SDK函数的标准形式,直截了当的创建方式,任何场合都可以使用。)
提供操作系统级别的创建线程的操作,且仅限于工作者线程。不调用MFC和RTL的函数时,可以用CreateThread,其它情况不要轻易。在使用的过程中要考虑到进程的同步与互斥的关系(防止死锁)。
线程函数定义为:DWORD WINAPI _yourThreadFun(LPVOID pParameter)。
但它没有考虑:
(1)C Runtime中需要对多线程进行纪录和初始化,以保证C函数库工作正常(典型的例子是strtok函数)。
(2)MFC也需要知道新线程的创建,也需要做一些初始化工作(当然,如果没用MFC就没事了)。  


_beginthreadex
MS对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常。然后,调用CreateThread真正创建线程。 仅使用Runtime Library时,可以用_BegingThread。


AfxBeginThread
MFC中线程创建的MFC函数,首先创建了相应的CWinThread对象,然后调用CWinThread::CreateThread,   在CWinThread::CreateThread中,完成了对线程对象的初始化工作,然后,调用_beginthreadex(AfxBeginThread相比较更为安全)创建线程。它简化了操作或让线程能够响应消息,即可用于界面线程,也可以用于工作者线程,但要注意不要在一个MFC程序中使用_beginthreadex()或CreateThread()。
线程函数定义为:UINT _yourThreadFun(LPVOID pParam)


=====================================================================






【参考2】CreateThread与_beginthreadex
=====================================================================


CreateThread
“CreateThread函数是用来创建线程的Windows函数不过,如果你正在编写C/C++代码,决不应该调用CreateThread。相反,应该使用Visual C++运行期库函数_beginthreadex。如果不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的CreateThred替代函数。不管这个替代函数是什么,你都必须使用。”


_beginthreadex
"_beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的,但是参数名和类型并不完全相同。这是因为 Microsoft的C/C++运行期库的开发小组认为, C/C++运行期函数不应该对Windows数据类型有任何依赖。_beginthreadex函数也像CreateThread那样,返回新创建的线程的句柄。因此,如果调用源代码中的CreateThread,就很容易用对_beginthreadex的调用全局取代所有这些调用。不过,由于数据类型并不完全相同,所以必须进行某种转换,使编译器运行得顺利些。" 
"下面是关于_beginthreadex的一些要点: 
1) 每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。(tiddata结构位于Mtdll.h文件中的Visual C++源代码中)。 
2) 传递给_beginthreadex的线程函数的地址保存在tiddata内存块中。传递给该函数的参数也保存在该数据块中。 
3) _beginthreadex确实从内部调用CreateThread,因为这是操作系统了解如何创建新线程的唯一方法。 
4) 当调用CreatetThread时,它被告知通过调用_threadstartex而不是pfnStartAddr来启动执行新线程。还有,传递给线程函数的参数是tiddata结构而不是pvParam的地址。 
5) 如果一切顺利,就会像CreateThread那样返回线程句柄。如果任何操作失败了,便返回NULL。"


为什么?
  “也许你想知道,如果调用CreateThread,而不是调用C/C++运行期库的_beginthreadex来创建新线程,将会发生什么情况。当一个线程调用要求tiddata结构的C/C++运行期库函数时,将会发生下面的一些情况(大多数C/C++运行期库函数都是线程安全函数,不需要该结构)。
  首先,C/C++运行期库函数试图(通过调用TlsGetValue)获取线程的数据块的地址。如果返回NULL作为tiddata块的地址,调用线程就不拥有与该地址相关的tiddata块。这时,C/C++运行期库函数就在现场为调用线程分配一个tiddata块,并对它进行初始化。然后该 tiddata块(通过TlsSetValue)与线程相关联。此时,只要线程在运行,该tiddata将与线程待在一起。这时,C/C++运行期库函数就可以使用线程的tiddata块,而且将来被调用的所有C/C++运行期函数也能使用tiddata块。 
  当然,这看来有些奇怪,因为线程运行时几乎没有任何障碍。不过,实际上还是存在一些问题。首先,如果线程使用C/C++运行期库的signal函数,那么整个进程就会终止运行,因为结构化异常处理帧尚未准备好。第二,如果不是调用_endthreadex来终止线程的运行,那么数据块就不会被撤消,内存泄漏就会出现(那么谁还为使用CreateThread函数创建的线程来调用_endthreadex呢?)。 
   注意如果程序模块链接到多线程DLL版本的C/C++运行期库,那么当线程终止运行并释放tiddata块(如果已经分配了tiddata块的话)时,该运行期库会收到一个DLL_THREAD_DETACH通知。尽管这可以防止tiddata块的泄漏,但是强烈建议使用_beginthreadex而不是使用Createthread来创建线程。


=====================================================================






【参考3】关于_beginthreadex和CreateThread的区别
=====================================================================


  在 Win32 API 中,创建线程的基本函数是 CreateThread,而 _beginthread(ex) 是C++ 运行库的函数。为什么要有两个呢?因为C++ 运行库里面有一些函数使用了全局量,如果使用 CreateThread 的情况下使用这些C++ 运行库的函数,就会出现不安全的问题。而 _beginthreadex 为这些全局变量做了处理,使得每个线程都有一份独立的“全局”量。
  所以,如果你的编程只调用 Win32 API/SDK ,就放心用 CreateThread;如果要用到C++ 运行时间库,那么就要使用 _beginthreadex ,并且需要在编译环境中选择 Use MultiThread Lib/DLL。
  通常他们的解释都是这容易造成内存泄漏。这个解释本身是没有错的,但是解释得不够完全和详细。以至于造成很多新手盲目的信任了那句话,在那里都是用_beginthreadex函数,或者是装作没有看到使用CreateThread函数。曾经有一段时间我也对这个问题很是困惑,不知道到底用那个才是对的。因为我不止一次在很多权威性的代码中看到对CreateThread函数的直接调用。难道是权威错了?? 抱着怀疑的态度查找了大量的资料和书籍,终于搞明白了这个问题的关键所在,在此做个说明,算是对那句话的一个完善。
  关于_beginthreadex和CreateThread的区别我就不做说明了,这个很容易找到的。我们只要知道一个问题:_beginthreadex是一个C运行时库的函数,CreateThread是一个系统API函数,_beginthreadex内部调用了CreateThread。只所以所有的书都强调内存泄漏的问题是因为_beginthreadex函数在创建线程的时候分配了一个堆结构并和线程本身关联起来,我们把这个结构叫做tiddata结构,是通过线程本地存储器TLS于线程本身关联起来。我们传入的线程入口函数就保存在这个结构中。tiddata的作用除了保存线程函数入口地址之外,还有一个重要的作用就是:C运行时库中有些函数需要通过这个结构来保存和获取一些数据,比如说errno之类的线程全局变量。这点才是最重要的。
  当一个线程调用一个要求tiddata结构的运行时库函数的时候,将发生下面的情况:
  运行时库函数试图TlsGetv alue获取线程数据块的地址,如果没有获取到,函数就会现场分配一个 tiddata结构,并且和线程相关联,于是问题出现了,如果不通过_endthreadex函数来终结线程的话,这个结构将不会被撤销,内存泄漏就会出现了。但通常情况下,我们都不推荐使用_endthreadex函数来结束线程,因为里面包含了ExitThread调用。
  找到了内存泄漏的具体原因,我们可以这样说:只要在创建的线程里面不使用一些要求tiddata结构的运行时库函数,我们的内存时安全的。所以,前面说的那句话应该这样说才完善:
  “绝对不要调用系统自带的CreateThread函数创建新的线程,而应该使用_beginthreadex,除非你在线程中绝不使用需要tiddata结构的运行时库函数”
  这个需要tiddata结构的函数有点麻烦了,在侯捷的《win32多线程程序设计》一书中这样说到:
   如果在除主线程之外的任何线程中进行一下操作,你就应该使用多线程版本的C runtime library,并使用_beginthreadex和_endthreadex:
   1 使用malloc()和free(),或是new和delete
   2 使用stdio.h或io.h里面声明的任何函数
   3 使用浮点变量或浮点运算函数
   4 调用任何一个使用了静态缓冲区的runtime函数,比如:asctime(),strtok()或rand()




=====================================================================








【参考4】_beginthreadex、CreateThread、AfxBeginThread的选择=====================================================================


1. Create/EndThread是Win32方法开始/结束一个线程
_beginthreadx/_endthreadex是C RunTime方式开始/结束一个线程
AfxBeginThread在MFC中开始/结束一个线程


2.直接在CreateThread API创建的线程中使用sprintf,malloc,strcat等涉及CRT存储堆操作的CRT库函数是很危险的,容易造成线程的意外中止。 在使用_beginthread和_beginthreadex创建的线程中可以安全的使用CRT函数。但是必须在线程结束的时候相应的调用_endthread或_endthreadex


3._beginthread成对调用的_endthread函数内部隐式的调用CloseHandle关闭了线程句柄,而与_beginthreadex成对使用的_endthreadex则没有关闭线程的句柄,需要显示的调用CloseHandle关闭线程句柄,不要使用_beginthread,使用._beginthreadex代替之。


4.尽量不要在一个MFC程序中使用_beginthreadex()或CreateThread()。


5.没有使用到MFC的线程尽量用_beginthreadex启动。


6.如果在一个与LIBCMT.LIB链接的程序中调用C Runtime函数,则必须要用_beginthreadex启动线程


7._beginthreadex启动的线程可以安全的调用任何C Runtime 函数


=====================================================================






【参考5】CreateThread()、_beginthread()以及_beginthreadex()联系与区别
=====================================================================


<<Windows核心编程>>中有很详细地介绍:
注意:若要创建一个新线程,绝对不要使用CreateThread,而应使用_beginthreadex.
Why?考虑标准C运行时库的一些变量和函数,如errno,这是一个全局变量。全局变量用于多线程会出什么事,你一定知道的了。故必须存在一种机制,使得每个线程能够引用它自己的errno变量,又不触及另一线程的errno变量._beginthreadex就为每个线程分配自己的tiddata内存结构。该结构保存了许多像errno这样的变量和函数的值、地址(自己看去吧)。
通过线程局部存储将tiddata与线程联系起来。具体实现在Threadex.c中有。   
结束线程使用函数_endthreadex函数,释放掉线程的tiddata数据块。   
CRT的函数库在线程出现之前就已经存在,所以原有的CRT不能真正支持线程,这导致我们在编程的时候有了CRT库的选择,在MSDN中查阅CRT的函数时都有:
  Libraries   
  LIBC.LIB   Single   thread   static   library,   retail   version     
  LIBCMT.LIB   Multithread   static   library,   retail   version     
  MSVCRT.LIB   Import   library   for   MSVCRT.DLL,   retail   version     

这样的提示!








  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值