Windows进程通信——创建线程

来源:互联网 发布:简单java小程序代码 编辑:程序博客网 时间:2024/06/04 19:12

1. 概述

在Windows环境下创建线程使用的方法主要由如下几种:

CreateThread:CreateThread是Windows的API函数,提供操作系统级别的创建线程的操作,且仅限于工作者线程。不调用MFC和RTL的函数时,可以用CreateThread,其它情况不要轻易使用。在使用的过程中要考虑到进程的同步与互斥的关系(防止死锁)。
beginthread,beginthreadex:MS的C/C++运行期库函数,是对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常。然后,调用CreateThread真正创建线程。
AfxBeginThread:MFC中线程创建的MFC函数,首先创建了相应的CWinThread对象,然后调用CWinThread::CreateThread,在CWinThread::CreateThread中,完成了对线程对象的初始化工作,然后,调用_beginthreadex(AfxBeginThread相比较更为安全)创建线程。它简化了操作或让线程能够响应消息,即可用于界面线程,也可以用于工作者线程,但要注意不要在一个MFC程序中使用_beginthreadex()或CreateThread()

2. 创建线程API和实例

1. CreateThread(),它的函数原型为

HANDLE CreateThread(  _In_  SEC_ATTRS        SecurityAttributes,  _In_  ULONG            StackSize,  _In_  SEC_THREAD_START StartFunction,  _In_  PVOID            ThreadParameter,  _In_  ULONG            CreationFlags,  _Out_ PULONG           ThreadId);
lpThreadAttributes:
指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE
dwStackSize:
设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小
lpStartAddress:
指向线程函数的指针
lpParameter:
向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL
dwCreationFlags :
线程标志,可取值如下:
CREATE_SUSPENDED(0x00000004):
创建一个挂起的线程,线程不立即执行,需要调用ResumeThread函数才能执行
0:
表示创建后立即激活
lpThreadId:
保存新线程的id

使用实例

DWORD WINAPI my_tic_count(LPVOID m_pParam);//必须定义成这种格式//CreateThread()调用的线程线程函数,没有延迟HANDLE m_hCreateThread = ::CreateThread(NULL,//使用默认的安全属性NULL,//使用的栈的大小,NULL使用创建线程进程的栈大小my_tic_count,//线程函数的函数地址&Total_num,//传递给线程的参数NULL,//线程的启动类型NULL);//新线程的id//方法1 CreateThread()调用的线程线程函数DWORD WINAPI my_tic_count(LPVOID m_pParam){cout << "CreateThread() count:" << endl;int* Total_num = (int*)m_pParam;for (int i=0; i<(*Total_num); ++i){count = i;cout << count << "\t";}cout << "\n";return 0;}

2. _beginthread(),它的函数原型为

uintptr_t _beginthread(void(*start_address)(void *),        unsigned stack_size,void *arglist);
start_address:
新线程的起始地址 ,指向新线程调用的函数的起始地址
stack_size:
新线程的堆栈大小,可以为0
arglist: 
传递给线程的参数列表,无参数是为NULL 

使用需要添加#include <process.h>,使用实例

void my_beginthread(void* m_pParam);//线程函数需要定义成这样HANDLE m_hbeginthread = (HANDLE)_beginthread(my_beginthread, NULL, &Total_num);//方法2 _beginthread()调用的线程线程函数void my_beginthread(void* m_pParam){cout << "_beginthread() count:" << endl;int* Total_num = (int*)m_pParam;for (int i = 0; i < (*Total_num); ++i){count = i;cout << count << "\t";}cout << "\n";}

3. _beginthreadex(),它的函数原型为

uintptr_t _beginthreadex( // NATIVE CODE   void *security,   unsigned stack_size,   unsigned ( __stdcall *start_address )( void * ),   void *arglist,   unsigned initflag,   unsigned *thrdaddr );
security:
安全属性,NULL为默认安全属性
stack_size:
指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0
start_address:
指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址)
arglist:
传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针
initflag:
线程初始状态,0:立即运行;CREATE_SUSPEND:suspended(悬挂)
thrdaddr:
用于记录线程ID的地址

使用实例:

unsigned int WINAPI my_beginthreadex(void* m_pParam);//线程函数需要定义成这样HANDLE m_hbeginthreadex = (HANDLE)_beginthreadex(NULL, NULL, my_beginthreadex, &Total_num, CREATE_SUSPENDED, NULL);//方法3 _beginthreadex()调用的线程线程函数unsigned int WINAPI my_beginthreadex(void* m_pParam){cout << "_beginthreadex() count:" << endl;int* Total_num = (int*)m_pParam;for (int i = 0; i < (*Total_num); ++i){count = i;cout << count << "\t";}cout << "\n";return 0;}

4. AfxBeginThread()。在MFC中,用户界面线程和工作者线程都是由AfxBeginThread创建的,MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别的函数原型为

工作线程:

CWinThread* AfxBeginThread(    AFX_THREADPROC pfnThreadProc,    LPVOID pParam,    int nPriority = THREAD_PRIORITY_NORMAL,    UINT nStackSize = 0,    DWORD dwCreateFlags = 0,    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL  );
pfnThreadProc : 
线程的入口函数,声明一定要如下: UINT MyThreadFunction(LPVOID pParam),不能设置为NULL
pParam : 
传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
nPriority : 
线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级.
nStackSize : 
指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈
dwCreateFlags : 
指定创建线程以后,线程有怎么样的标志.可以指定两个值:
CREATE_SUSPENDED : 
线程创建以后,会处于挂起状态,直到调用:ResumeThread
0 : 
创建线程后就开始运行.
lpSecurityAttrs : 
指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL
返回值:
一个指向新线程的线程对象的指针

界面线程:

CWinThread* AfxBeginThread(    CRuntimeClass* pThreadClass,    int nPriority = THREAD_PRIORITY_NORMAL,    UINT nStackSize = 0,    DWORD dwCreateFlags = 0,    LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL  );
pThreadClass:
从CWinThread派生的RUNTIME_CLASS类;
nPriority:
指定线程优先级,如果为0,则与创建该线程的线程相同;
nStackSize:
指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
dwCreateFlags:
一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
lpSecurityAttrs:
表示线程的安全属性,NT下有用。

使用实例

UINT MyThreadFunction(LPVOID m_pParam);::AfxBeginThread(MyThreadFunction, NULL, NULL, NULL, NULL);//方法4 AfxBeginThread()调用的线程线程函数UINT MyThreadFunction(LPVOID m_pParam){cout << "_beginthreadex() count:" << endl;int* Total_num = (int*)m_pParam;for (int i = 0; i < (*Total_num); ++i){count = i;...}return 0;}

补充:
在上面的示例中我都将返回值转换为了HANDLE类型,目的无非是为了得到现成的资源标识,进而方便进行管理。在对线程进行管理的时候这里使用的API主要有ResumeThread函数、SuspendThread函数和ExitThread()函数,这些函数在字面意思已经很清楚了,分别是回复线程、线程暂停、结束线程。但是在实际使用过程中下面的两点需要注意:

(1)对于ResumeThread函数
在暂停状态中创建一个线程,就能够在线程有机会执行任何代码之前改变线程的运行环境(如优先级)。一旦改变了线程的环境,必须使线程成为可调度线程。要进行这项操作,可以调用ResumeThread,将调用CreateThread函数时返回的线程句柄传递给它(或者是将传递给CreateProcess的ppiProcInfo参数指向的线程句柄传递给它):如果ResumeThread函数运行成功,它将返回线程的前一个暂停计数,否则返回0xFFFFFFFF。单个线程可以暂停若干次。如果一个线程暂停了3次,它必须恢复3次,然后它才可以被分配给一个CPU。 
(2)对于SuspendThread函数
当创建线程时,除了使用 CRETE_SUSPENDED也可以调用SuspendThread函数来暂停线程的运行:任何线程都可以调用该函数来暂停另一个线程的运行(只要拥有线程的句柄)。线程可以自行暂停运行,但是不能自行恢复运行。与ResumeThread一样SuspendThread返回的是线程的前一个暂停计数。线程暂停的最多次数可以是MAXIMUM_SUSPEND_COUNT次(在WinNT. h中定义为127)。注意,SuspendThread与内核方式的执行是异步进行的,但是在线程恢复运行之前,不会发生用户方式的执行。在实际环境中,调用SuspendThread时必须小心,因为不知道暂停线程运行时它在进行什么操作。如果线程试图从堆栈中分配内存,那么该线程将在该堆栈上设置一个锁。当其他线程试图访问该堆栈时,这些线程的访问就被停止,直到第一个线程恢复运行。

原创粉丝点击