多线程创建方法总结

来源:互联网 发布:linux 查看cpu详解 编辑:程序博客网 时间:2024/06/05 18:14

1、CreateThread()函数是Windows提供的API接口

HANDLE CreateThread(

 LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD

DWORD dwStackSize,                        // initial stack size

 LPTHREAD_START_ROUTINE lpStartAddress,    // thread function

 LPVOID lpParameter,                       // thread argument

 DWORD dwCreationFlags,                    // creation option

LPDWORD lpThreadId                        // thread identifier

);

函数说明:

第一个参数表示线程内核对象的安全属性,一般传入NULL表示使用默认设置。

If lpThreadAttributes is NULL, the handle cannot be inherited.

Windows NT/2000: The lpSecurityDescriptor member of the structure specifies a security descriptor for the new thread. If lpThreadAttributes is NULL, the thread gets a default security descriptor. 

 

第二个参数表示线程栈空间大小。传入0表示使用默认大小(1MB)。

Specifies the initial commit size of the stack, in bytes. The system rounds this value to the nearest page. If this value is zero, or is smaller than the default commit size, the default is to use the same size as the calling thread. For more information, see Thread Stack Size.

 

第三个参数表示新线程所执行的线程函数地址多个线程可以使用同一个函数地址。

第四个参数是传给线程函数的参数。

第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。

第六个参数将返回线程的ID号,传入NULL表示不需要返回该线程ID号。

函数返回值:

成功返回新线程的句柄,失败返回NULL。 


 

CreateThread()函数是Windows提供的API接口,在C/C++语言另有一个创建线程的函数_beginthreadex(),在很多书上(包括《Windows核心编程》)提到过尽量使用_beginthreadex()来代替使用CreateThread(),这是为什么了?下面就来探索与发现它们的区别吧。

首先要从标准C运行库与多线程的矛盾说起,标准C运行库在1970年被实现了,由于当时没任何一个操作系统提供对多线程的支持。因此编写标准C运行库的程序员根本没考虑多线程程序使用标准C运行库的情况。比如标准C运行库的全局变量errno。很多运行库中的函数在出错时会将错误代号赋值给这个全局变量,这样可以方便调试。但如果有这样的一个代码片段:

if (system("notepad.exe readme.txt") = = -1)

{

switch(errno)

{

...//错误处理代码

}

}

 

假设某个线程A在执行上面的代码,该线程在调用system()之后且尚未调用switch()语句时另外一个线程B启动了,这个线程B也调用了标准C运行库的函数,不幸的是这个函数执行出错了并将错误代号写入全局变量errno中。这样线程A一旦开始执行switch()语句时,它将访问一个被B线程改动了的errno这种情况必须要加以避免!因为不单单是这一个变量会出问题,其它像strerror()、strtok()、tmpnam()、gmtime()、asctime()等函数也会遇到这种由多个线程访问修改导致的数据覆盖问题。

 

为了解决这个问题,Windows操作系统提供了这样的一种解决方案——每个线程都将拥有自己专用的一块内存区域来供标准C运行库中所有有需要的函数使用。而且这块内存区域的创建就是由C/C++运行库函数_beginthreadex()来负责的。下面列出_beginthreadex()函数的源代码(我在这份代码中增加了一些注释)以便读者更好的理解_beginthreadex()函数与CreateThread()函数的区别。

 

2、unsigned long _beginthreadex( void *security

unsigned stack_size

unsigned ( __stdcall *start_address )( void * ),

void *arglist

unsigned initflag

unsigned *thrdaddr );

security:Security descriptor for new thread; must be NULL for Windows 95 applications

stack_size:Stack size for new thread or 0

start_address:Start address of routine that begins execution of new thread

arglist:Argument list to be passed to new thread or NULL

initflag:Initial state of new thread (0 for running or CREATE_SUSPEND for suspended)

thrdaddr:Points to a 32-bit variable that receives the thread identifier


3、Win32 API提供了_beginthreadex()函数,可以让用户在底层启动线程。总的来说,Win32 API提供的线程处理的借口引用较为复杂。因此,在Win32 API的基础上,MFC提供了处理线程的类和函数。其中,MFC提供处理线程的类为CWinThread类。

一般来说,用户可以直接声明CWinThread对象,但在许多情况下,可以让MFC的全局函数AfxBeginThread来创建CWinThread对象。

MFC中有两类线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。

  

     工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程执行之外 的用户输入,响应用户及系统所产生的事件和消息等。但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来 执行任务。

 

    在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。两种重载函数原型和参数分别说明如下: 

(1) CWinThread* AfxBeginThread(

AFX_THREADPROC pfnThreadProc,                      

LPVOID pParam,                      

nPriority=THREAD_PRIORITY_NORMAL,                      

UINT nStackSize=0,                      

DWORD dwCreateFlags=0,                      

LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

PfnThreadProc:指向工作者线程的执行函数的指针,线程函数原型必须声明如下:     

UINT ExecutingFunction(LPVOID pParam);       

请注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。 

pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略; 

nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级; 

nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果nStackSize被设为0,则线程的堆栈被设置成与父线程堆栈相同大小; 

dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起; 

lpSecurityAttrs:线程的安全属性指针,一般为NULL; 

(2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass, 

int nPriority=THREAD_PRIORITY_NORMAL,                      

UINT nStackSize=0,                      

DWORD dwCreateFlags=0,                      

LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同形式1。使用函数的这个原型生成的线程也有消息机制,此消息机制同主线程的机制几乎一样。

 

在工作线程中使用的函数指针一般是指向全局函数的而不是类成员函数,因为这牵扯到对象的生命周期,如果一个对象在线程执行时被销毁了,那么这个线程的行为就成为不确定的了。

4、说了windows下的线程创建,接下来就是liunx下的线程创建了

int pthread_create(

pthread_t*    thread,

pthread_attr_t   *attr,

void (*start_routine)(void *arg),

void *arg);

thread :程序的ID(标示符)。

attr:线程的属性设置,如果为NULL,则创建系统默认的线程属性。

start_routine:线程运行函数的起始地址。

最后一个参数是:线程运行函数的参数。


 

 

 

0 0
原创粉丝点击