您的位置:首页 > 其它

Windows进程通信——创建线程

2017-06-08 11:47 525 查看

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时必须小心,因为不知道暂停线程运行时它在进行什么操作。如果线程试图从堆栈中分配内存,那么该线程将在该堆栈上设置一个锁。当其他线程试图访问该堆栈时,这些线程的访问就被停止,直到第一个线程恢复运行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: