begingthread afxbeginthread createthread

在微软的 Programming Techniques 说明文件中有一句看似悲惨的警告:

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

 

首先看一下_beginthreadex:

1、如果你写一个多线程程序,而且没有使用MFC,那么你应该总是和多线程版本的C Run time library进行链接。并且应该总是以_beginthreadex和_endthreadex取代createthread和exitthread。_beginthread参数与createthread一致并且对C Runtime library进行了必要的初始化。

2、关于C Runtime library的多线程版本:之前的版本不能被多线程的应用程序所使用。由于它使用了很多全局变量以及静态变量,所以在多线程应用程序中很容易产生同步错误。因为这个原因,所以产生了C Runtime library的多线程版本。

3、你可以将_beginthreadex理解为createthread的外包函数,它记录了一些和启动线程相关的信息。

4、unsigned long _beginthreadex(   

void *security,  //安全属性

unsigned stack_size,  //堆栈大小 

unsigned (__stdcall *start_address)(void *),  //回调函数   

void *arglist,unsigned initflag,  //创建标志  

unsigned* thrdaddr  //线程ID

);

 

一个简单的例子:

#include <iostream>
#include <cstdlib>
using namespace std;
#include <Windows.h>
#include <process.h>

 

unsigned _stdcall ThreadProc(void* param);

int main()
{
 HANDLE handle = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, NULL, NULL);
 if (handle == NULL)
 {
  cout<<"create thread failed"<<endl;
  system("pause");
  return 0;
 }
 WaitForSingleObject(handle, INFINITE);
 CloseHandle(handle);
 system("pause");
 return 0;
}

unsigned _stdcall ThreadProc(void* param)
{
 cout<<"_beginthreadex create thread"<<endl;
 return 0;
}

 

如果一个多线程程序在worker线程中不会调用c runtime library多线程版本中函数的话,应该能够使用单线程版本的c runtime library并且可以通过createthread和exitthread来创建和结束线程。然而C程序不调用任何多线程版本的c runtime函数通常是不可能的,所以记住这一点:如果要产生一个多线程的C程序(不使用MFC),那么请一定使用_beginthreadex和_endthreadex,而不要使用createthread和_beginthread。

关于_beginthread:它被认为是一个头脑简单的函数,它并没有获得和createthread函数完全一样的参数,所以有些事情它办不到,比如说线程的挂起状态。另外重要的一点是,它所产生出来的线程所做的第一件事情就是关闭掉handle,所以由它所返回的handle可能可用,也可能不可用。



<<Windows核心编程>>中有很详细地介绍。我再说一些吧
_beginthreadex是微软的C/C++运行时库函数,CreateThread是操作系统的函数。
_beginthreadex通过调用CreateThread来实现的,但比CreateThread多做了许多工作。
注意:若要创建一个新线程,绝对不要使用CreateThread,而应使用_beginthreadex.
Why?考虑标准C运行时库的一些变量和函数,如errno,这是一个全局变量。全局变量用于
多线程会出什么事,你一定知道的了。故必须存在一种机制,使得每个线程能够引用它自己的
errno变量,又不触及另一线程的errno变量._beginthreadex就为每个线程分配自己的
tiddata内存结构。该结构保存了许多像errno这样的变量和函数的值、地址(自己看去吧)。
通过线程局部存储将tiddata与线程联系起来。具体实现在Threadex.c中有。
结束线程使用函数_endthreadex函数,释放掉线程的tiddata数据块。



4.线程:资源调度的最小单位
  线程的构成:
    (1)内核对象:存放线程相关信息
    (2)线程堆栈:维护执行代码时所需的参数和变量
  通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。
 
下面看看C++创建进程的相关函数:
复制代码
1 HANDLE WINAPI CreateThread(
2   __in          LPSECURITY_ATTRIBUTES lpThreadAttributes,
3   __in          SIZE_T dwStackSize,
4   __in          LPTHREAD_START_ROUTINE lpStartAddress,
5   __in          LPVOID lpParameter,
6   __in          DWORD dwCreationFlags,
7   __out         LPDWORD lpThreadId
8 );
复制代码
复制代码
1 uintptr_t _beginthreadex( 
2    void *security,
3    unsigned stack_size,
4    unsigned ( *start_address )( void * ),
5    void *arglist,
6    unsigned initflag,
7    unsigned *thrdaddr 
8 );
复制代码

两个函数都是用于创建线程,第一个是Windows API函数,在WinBase.h头文件中,第二个不是API函数,在process.h头文件中
参数说明:

1.线程安全性:表示是否可以被子进程所继承

2.初始堆栈大小:如果为0或者小于默认值,则使用和调用线程同样大小的空间

3.线程其实地址:一个函数指针,指向线程函数

4.参数:传递给线程函数的参数

5.创建选项:如果为CREATE_SUSPENDED表示创建后挂起,如果为0表示创建后立即执行

6.线程ID

两个函数的区别:

  malloc、fopen、ctime等函数需要专门的线程局部存储数据块,这个数据块在创建线程时创建。如果用CreateThread,则不会创建,这样,函数能够正常使用,但是会自动创建数据块,但是函数并不会释放创建的数据库,所以并不会将其删除,就导致内存泄露!!!

  而_beginthreadex(内部也调用CreateThread)和_beginthreadex(会自动调用CloseHandle关闭句柄)对这个内存块做了处理。

 

代码演示:

复制代码
 1 #include <iostream>
 2 #include <Windows.h>
 3 #include <process.h>
 4 using namespace std;
 5 
 6 
 7 DWORD WINAPI CreateFun(LPVOID lParam)
 8 {
 9     cout << "CreateThread" << endl;
10     return 0;//0表示成功
11 
12 }
13 
14 UINT _stdcall beginFun(LPVOID lParam)
15 {
16     cout << "beginthreadex" << endl;
17     return 0;
18 }
19 int main(void)
20 {
21 
22     DWORD dwID;
23     UINT nID;
24     HANDLE hC;
25     HANDLE hB;
26 
27     hC = CreateThread(NULL, 0, CreateFun, NULL, 0, &dwID);
28 
29     if (NULL != hC)
30     {
31         CloseHandle(hC);
32     }
33 
34 
35 
36     hB = (HANDLE)_beginthreadex(NULL, 0, beginFun, NULL, 0, &nID);
37     if (NULL != hB)
38     {
39         CloseHandle(hB);
40     }
41 
42     Sleep(1000);
43 }
复制代码

 

 CloseHandle:关闭句柄

  调用CloseHandle并不会终止线程的执行,而是递减线程内核对象句柄计数,线程执行完毕后也会自动递减,当计数为0时释放线程内核对象。当进程终止时也会清理内核对象。

但是,如果不关闭,可能导致有些进程拥有的资源无法释放,导致内存泄露。

线程的相关函数:

(1)CreateThread:创建线程,失败返回NULL,成功返回线程句柄

(2)SuspendThread:挂起线程

(3)ResumeThread:恢复线程

(4)OpenThread:打开线程,根据线程ID得到线程句柄

(5)ExitThread:退出线程

(6)TerminateThread:终止线程

(7)GetExitCodeThread:获取线程运行状态,如果为STILL_ALIVE表示正在运行。

(8)GetCurrentThread:获取当前线程句柄

(9)GetCurentThreadID:获取当前线程ID

 注意:最好不要显式的调用ExitThread和TerminateThread,因为可能导致线程无法清理某些东西,导致内存泄露~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值