网络编程(42)—— windows平台下创建线程的四种方法(一)

一、引言

       接下来,笔者主要谈下Windows平台下多线程的用法。首先,要强调一点,Windows平台的线程linux中的线程不同,它会伴着主函数的结束而销毁。Windows平台下可用的创建多线程的API共有四个,分别是:

(1) CreateThread()

        CreateThread是Windows的API函数,提供操作系统级别的创建线程的操作,且仅限于工作者线程。在MFC中不要使用。

(2) _beginthreadex()

        _beginthreadex 是微软C/C++运行时库函数,线程安全标准的C函数,它针对C的运行时库做了一些初始化的工作,以保证C运行时库能够正常使用。然后,内部调用CreateThread创建线程。

(3) _beginthread()

        _beginthread相对_beginthreadex来说参数比较少,如无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程的ID值。特别是调用beginthreadex时,它会让返回的线程句柄值失效,用来防止用户直接访问内核对象

(4) AfxBeginThread()

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


注意:
1、CreateThread函数是用来创建线程的Windows函数。不过,如果你正在编写C/C++代码,不应该调用 CreateThread。相反,应该使用Visual C++运行时库函数_beginthreadex。如果不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的CreateThred替代函数。

2、 线程的句柄不同于线程的ID,句柄是在同一个进程中区分内核对象的标志,同一个进程中一个内核对象的句柄值是唯一的,但是不同进程之间句柄值可能相等;而ID在跨进程中也是保持唯一的。


二、CreateThread

函数的原型如下:

HANDLE CreateThread(
   LPSECURITY_ATTRIBUTES lpsa,
   DWORD dwStackSize,
   LPTHREAD_START_ROUTINE pfnThreadProc,
   void* pvParam,
   DWORD dwCreationFlags,
   DWORD* pdwThreadId 
)

sa —— 线程的安全属性,默认情况下设置为NULL。

dwStackSize —— 为线程分配的调用堆栈的大小,传递0时会采用默认的大小。

pfnThreadProc —— 线程函数,默认声明格式为unsigned WINAPI ThreadFunc(void* arg);

pvParam —— 线程函数的参数指针,void*类型。

dwCreationFlags —— 创建后线程的状态,0表示创建后立即执行,使用CREATE_SUSPENDED宏,表示创建后被立即挂起,直到使用ResumeThread可以再次激活线程。

pdwThreadId —— 保存线程ID的指针,如果不需要获取线程ID,填写NULL即可。


        下面的代码,我们来使用CreateThread创建线程,并赋予线程不同的状态,来介绍CreateThread的用法。
#include "stdafx.h"
#include "Windows.h"
#include "stdio.h"

unsigned WINAPI ThreadFunc1(void* pargs)
{
	while (1)
	{
		puts("ThreadFunc1 Run ...");
		Sleep(1000);
	}
	return 0;
}
unsigned WINAPI ThreadFunc2(void* pargs)
{
	while(1)
	{
		puts("ThreadFunc2 Run ...");
		Sleep(1000);
	}
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	//创ä¡ä建¡§一°?个?线?程¨¬初?始º?状Á¡ä态¬?为a运?行D
	HANDLE hThread1 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc1,NULL,0,NULL);
	//创ä¡ä建¡§另¢¨ª一°?个?线?程¨¬,ê?初?始º?状Á¡ä态¬?为a挂¨°起e
	HANDLE hThread2 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc2,NULL,CREATE_SUSPENDED,NULL);

	Sleep(5000);
	puts("ResumeThread ...");
	ResumeThread(hThread2);
	Sleep(10000);
	return 0;
}

第5行和第14行,分别创建了两个线程函数ThreadFunc1和ThreadFunc2,每个线程函数中都每个1秒钟循环打印各自的字符串。

第27行和29行,分别创建了两个线程,hThread1创建时激活,hThread2创建时被挂起,知道sleep 5 秒后被ResumeThread函数唤醒。

我们期望的最终的效果就是ThreadFunc1先输出5个字符串,5秒之后,ThreadFunc2和ThreadFunc1各自输出10个字符串,以下是输出结果:




三、_beginthreadex

uintptr_t _beginthreadex( 
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr 
);

        _beginthreadex()的参数和CreateThread的参数几乎一模一样,这里就不再重复解释,验证_beginthreadex时,我们只需要将原来的代码中CreateThread换成

_beginthreadex,再微调一下代码:

#include "stdafx.h"
#include "Windows.h"
#include "stdio.h"
#include "process.h"

unsigned WINAPI ThreadFunc1(void* pargs)
{
	while (1)
	{
		puts("ThreadFunc1 Run ...");
		Sleep(1000);
	}
	return 0;
}
unsigned WINAPI ThreadFunc2(void* pargs)
{
	while(1)
	{
		puts("ThreadFunc2 Run ...");
		Sleep(1000);
	}
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	//创ä¡ä建¡§一°?个?线?程¨¬初?始º?状Á¡ä态¬?为a运?行D
	HANDLE hThread1 = (HANDLE)_beginthreadex(NULL,0,ThreadFunc1,NULL,0,NULL);
	//创ä¡ä建¡§另¢¨ª一°?个?线?程¨¬,ê?初?始º?状Á¡ä态¬?为a挂¨°起e
HANDLE hThread2 = (HANDLE)_beginthreadex(NULL,0,ThreadFunc2,NULL,CREATE_SUSPENDED,NULL);

	Sleep(5000);
	puts("ResumeThread ...");
	ResumeThread(hThread2);
	Sleep(10000);
	return 0;
}

从上面的代码可以看出,将CreateThread换成_beginthreadex,我们主要修改的地方有三处:

1、 增加了头文件processs.h

2、 _beginthreadex的返回值需要强转成HANDLE。

3、 传入的线程函数不再需要LPTHREAD_START_ROUTINE进行强转。


四、beginthread

beginthread的函数原型如下:

uintptr_t _beginthread( 
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist 
);


        可以看出来,相比较_beginthreadex而言,它的参数要少得多。只有线程函数的地址start_address,分配堆栈的大小stack_size,以及线程传递的参数arglist。我们使用

beginthread的例子很简单,就是创建一个线程函数打印主函数传递过来的字符串:

#include "stdafx.h"
#include "Windows.h"
#include "stdio.h"
#include "process.h"

void ThreadFunc(void* pargs)
{
	char* str=(char*)pargs;
	puts(str);
}

int _tmain(int argc, _TCHAR* argv[])
{
	char* str="我是主线程传进来的字符串”;
	HANDLE hThread1 = (HANDLE)_beginthread(ThreadFunc,0,(void*)str);
	Sleep(5000);
	return 0;
}
        运行结果:


Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
Git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL42

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值