IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> (_beginthread、_beginthreadex)和CreatThread -> 正文阅读

[网络协议](_beginthread、_beginthreadex)和CreatThread

_beginthreadex和CreatThread

函数介绍

CreateThread

先从比较常见的CreatThread说起,CreateThread函数创建一个在调用进程的地址空间内执行的线程。

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES 【lpThreadAttributes】,	//指向线程安全属性的指针
    DWORD 【dwStackSize】,	//初始线程堆栈大小,以字节为单位
    LPTHREAD_START_ROUTINE 【lpStartAddress】,	//指向线程函数的指针
    LPVOID 【lpParameter参数】,	//参数新线程
    DWORD 【dwCreationFlags】,	//创建标志
    LPDWORD 【lpThreadId】	//指向返回的线程标识符
);

Win32API参考手册

返回值

如果函数成功,则返回值是新线程的句柄。

如果函数失败,返回值为NULL。要获取扩展错误信息,请调用GetLastError.

CreateThread Demo:

/* 创建第一个线程。主进程结束,则撤销线程。 */

#include<Windows.h>
#include<stdio.h>

DWORD WINAPI ThreadFunc(LPVOID);

void main()
{
	HANDLE hThread;
	DWORD  threadId;

	hThread = CreateThread(NULL, 0,	ThreadFunc, 0, 0, &threadId); // 创建线程
	printf("我是主线程, pid = %d\n", GetCurrentThreadId());  //输出主线程pid
	Sleep(2000);
}

DWORD WINAPI ThreadFunc(LPVOID p)
{	
	printf("我是子线程, pid = %d\n", GetCurrentThreadId());   //输出子线程pid
	return 0;
}


终止线程

《window核心编程》一书中提到,如要终止线程的运行,可以使用下面的方法:

  • 线程函数返回(最好使用这种方法)
  • 通过调用ExitThread哈桑农户,线程将自行撤销(最好不要使用这种方法)
  • 同一个进程或另一个进程中的线程调用TerminateThread函数(应该避免使用这种方法)。
  • 包含线程的进程终止运行。

_beginthreadex_beginthread

C++ 运行期库有两个创建线程的函数,一个是_beginthread,另一个是 _beginthreadex

创建线程函数接口如下:


unsigned long _beginthread(
  void(_cdecl *start_address)(void *), //声明为void (*start_address)(void *)形式 这个参数即为函数名
  unsigned stack_size, //是线程堆栈大小,一般默认为0(表示与主线程使用一样的堆栈)
  void *arglist //向线程传递的参数,一般为结构体,没有参数时设置为NULL
);

//推荐使用
unsigned long _beginthreadex(
  void *security,	//安全属性,NULL表示默认安全性
  unsigned stack_size, //是线程堆栈大小,一般默认为0
  unsigned(_stdcall  *start_address)(void *),	//声明为unsigned(*start_address)(void *)形式
  void *argilist,	//向线程传递的参数,一般为结构体
  unsigned initflag, //新线程的初始状态,0表示立即执行,CREATE_SUSPEND表示创建后挂起。
  unsigned *thrdaddr //该变量存放线程标识符,它是CreateThread函数中的线程ID。
};

线程结束:

//释放线程空间、释放线程TLS空间、调用ExiteThread结束线程。
void _endthread(void); 	

// retval:设定的线程结束码,与ExiteThread函数的参数功能一样,
//其实这个函数释放线程TLS空间,再调用ExiteThread函数,但没有释放线程空间。
void _endthreadex(unsigned retval);

两者的区别在于:

  1. 参数形式不同
  2. ex能够创建悬挂状态线程,在nt中能够指定级别,能够被thrdaddr访问,因为它有了id
  3. ex使用__stdcall调用格式,必须返回exit code
  4. ex返回0代表失败,原先的返回-1L。
  5. ex创建的必须用ex销毁

_beginthreadDemo:

程序最后的system("pause")必须有,因为_begeinThread调用时,参数2的值为0,即子线程是挂靠在主线程的,当主线程结束时,子线程也被迫结束,由于主线程执行时间过于短暂,因此需要使用该语句控制线程处于暂停状态,另外,由于三个线程的问题,为了控制打印的次序,在线程中也加了Sleep()函数控制执行的时刻。

例子也包括了_begeinThread如何传参。


#include <iostream>
#include <windows.h>
#include <process.h>
#include <string.h>
using std::cout;
using std::endl;

typedef struct _STU_PEOPLE
{
	char szName[50];
	int age;
}STU_PEOPLE,*PSTU_PEOPLE;

void thread_1(void *)
{
	
	cout << "调用了线程 thread_1 无进入参数" << endl;
}

void thread_2(void *lpVoid)
{
	Sleep(10);
	int pnInt = (int)lpVoid;
	cout << "调用了线程 thread_2 进入参数为:" << pnInt << endl;
}

void thread_3(void *lpVoid)
{
	Sleep(50);
	PSTU_PEOPLE pnInt = (PSTU_PEOPLE)lpVoid;
	cout << "调用了线程 thread_3 进入参数为:" << pnInt->szName << ':' << pnInt->age << endl;
}


int main()
{
	STU_PEOPLE stuChild;

	char name[] = { "Child_LI" };
	strcpy_s(stuChild.szName, name);
	stuChild.age = 6;


    std::cout << "Start Thread!\n";
	_beginthread(thread_1, 0, NULL);
	_beginthread(thread_2, 0, (void*)stuChild.age);
	_beginthread(thread_3, 0, (void*)&stuChild);

	std::cout << "End Thread!\n";

	Sleep(100);
	system("pause");
}

输出:
在这里插入图片描述

需要注意的是,Sleep()函数并不能保证线程严格按照规定的时间运行。

1. 当我们用_beginthread()函数创建一个线程之后,这个线程将马上伺机执行,但是需要等待CPU为其分配资源,线程执行的顺序是不一定的(完全有可能最先创建的线程最后执行);

2. main函数是主线程函数,在main函数中创建的线程为子线程。在主线程结束后子线程将被迫停止,因此子线程实际上不会被执行;

3. 为了执行子线程,可以将主线程一直运行,如while(1);或者system("pause"); 因此在写项目代码时,一定不要忘记在线程的函数中添加while(1)循环

也可以将某个线程强制休眠,用Sleep()函数。Sleep()函数将指定某个线程至少休眠多长时间。

区分

在 Win32 API 中,创建线程的基本函数是 CreateThread,而 _beginthread(ex)是 C++ 运行库的函数。为什么要有两个呢?

因为C++ 运行库里面有一些函数使用了全局 量,如果使用 CreateThread 的情况下使用这些C++ 运行库的函数,就会出现不安全 的问题。而 _beginthreadex 为这些全局变量做了处理,使得每个线程都有一份独立 的“全局”量。

所以,如果你的编程只调用 Win32 API/SDK ,就放心用 CreateThread;如果要用到 C++ 运行时间库,那么就要使用_beginthreadex,并且需要在编译环境中选择 Use MultiThread Lib/DLL。

参考:

  • Win32API手册
  • C++手册
  • 网络
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-12-23 16:03:14  更:2021-12-23 16:03:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 12:51:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码