C/C++中线程基本概念与创建详解

 更新时间:2022年9月16日 00:21  点击:20 作者:霸道小明

一、线程基本概念

线程是在进程中产生的一个执行单元,是CPU调度和分配的最小单元,其在同一个进程中与其他线程并行运行,他们可以共享进程内的资源,比如内存、地址空间、打开的文件等等。

线程是CPU调度和分派的基本单位,

进程是分配资源的基本单位

进程:正在运行的程序

是处于执行期的程序以及它所管理的资源(如打开的文件、挂起的信号、进程状态、地址空间等等)的总称,从操作系统核心角度来说,进程是操作系统调度除CPU时间片外进行的资源分配和保护的基本单位,它有一个独立的虚拟地址空间,用来容纳进程映像(如与进程关联的程序与数据),并以进程为单位对各种资源实施保护,如受保护地访问处理器、文件、外部设备及其他进程(进程间通信)

计算机有很多资源组成,比如CPU、内存、磁盘、鼠标、键盘等,就像一个工厂由电力系统、作业车间、仓库、管理办公室和工人组成 

假定工厂的电力有限,一次只能供给一个或少量几个车间使用。也就是说,一部分车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务,多个CPU能够运行少量任务。 

线程就好比车间里的工人。一个进程可以包括多个线程,他们协同完成某一个任务。

二、为什么使用多线程

1.避免阻塞

大家知道,单个进程只有一个主线程,当主线程阻塞的时候,整个进程也就阻塞了,无法再去做其它的一些功能了。

2.避免CPU空转

应用程序经常会涉及到RPC,数据库访问,磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应时,CPU却不能去处理新的请求,导致这种单线程的应用程序性能很差。

3.提升效率

一个进程要独立拥有4GB的虚拟地址空间,而多个线程可以共享同一地址空间,线程的切换比进程的切换要快得多。

上下文切换

三、创建线程函数

1.CreateThread

CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD
    SIZE_T dwStackSize,//initialstacksize
    LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction
    LPVOID lpParameter,//threadargument
    DWORD dwCreationFlags,//creationoption
    LPDWORD lpThreadId//threadidentifier
)

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

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

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

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

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

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

2._beginthreadex

unsigned long _beginthreadex(
    void *security,    // 安全属性, 为NULL时表示默认安全性
    unsigned stack_size,    // 线程的堆栈大小, 一般默认为0
    unsigned(_stdcall *start_address)(void *),   // 线程函数
    void *argilist, // 线程函数的参数
    unsigned initflag,    // 新线程的初始状态,0表示立即执行,//CREATE_SUSPENDED表示创建之后挂起
    unsigned *threaddr    // 用来接收线程ID
 
);

返回值 : // 成功返回新线程句柄, 失败返回0

__stdcall表示

1.参数从右向左压入堆栈

2.函数被调用者修改堆栈

四、简单多线程示例

现在有三个任务,Tom每隔3秒捉一次Jerry,Jerry每隔2秒吃一次奶酪,Spike每隔1秒打一次Tom。分别用CreateThread和_beginthreadex实现

使用_beginthreadex

#include<stdio.h>
#include<Windows.h>
#include<process.h>
 
//Tom每隔3秒捉一次老鼠
unsigned WINAPI thread_main_Tom(void* arg) {
	int cnt = *((int*)arg);
	for (int i = 0; i < cnt; i++) {
		Sleep(3000);
		puts("Tom 捉老鼠\n");
	}
	return 0;
}
//Jerry每隔1秒吃一次奶酪
unsigned WINAPI thread_main_Jerry(void* arg) {
	int cnt = *((int*)arg);
	for (int i = 0; i < cnt; i++) {
		Sleep(1000);
		puts("Jerry 吃奶酪\n");
	}
	return 0;
}
//Spike每隔2秒打一次猫
unsigned WINAPI thread_main_Spike(void* arg) {
	int cnt = *((int*)arg);
	for (int i = 0; i < cnt; i++) {
		Sleep(2000);
		puts("Spike 打猫\n");
	}
	return 0;
}
 
int main() {
	int Tom = 20, Jerry = 50, Spike = 40;
    //保存线程Id
	unsigned int Tom_id, Jerry_id, Spike_id;
    //创建线程
	_beginthreadex(NULL, 0, thread_main_Tom, (void*)&Tom, 0, &Tom_id);
	_beginthreadex(NULL, 0, thread_main_Jerry, (void*)&Jerry, 0, &Jerry_id);
	_beginthreadex(NULL, 0, thread_main_Spike, (void*)&Spike, 0, &Spike_id);
	system("pause");
	return 0;
}

运行结果:

使用CreateThread

#include<stdio.h>
#include<Windows.h>
#include<process.h>
 
//DWORD就是unsigned long
//LPVOID是void*
DWORD _stdcall ThreadFun(LPVOID p) {
 
	printf("我是子线程,PID=%d", GetCurrentThreadId());
	return 0;
}
 
int main() {
	printf("main begin\n");
	HANDLE hThead;
	DWORD dwThreadID;
	hThead = CreateThread(NULL, 0, ThreadFun, 0, 0, &dwThreadID);
	printf("我是主线程,PID=%d\n",GetCurrentThreadId());
	Sleep(2000);
	//关闭线程
	if (hThead) {
		CloseHandle(hThead);
	}
	system("pause");
	return 0;
}

运行结果:

到此这篇关于C/C++中线程基本概念与创建详解的文章就介绍到这了,更多相关C++线程内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

原文出处:https://blog.csdn.net/qq_54169998/article/details/126813473

相关文章

  • C++中取余运算的实现

    这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • C++万能库头文件在vs中的安装步骤(图文)

    这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • C#实现跨线程操作控件方法

    这篇文章主要介绍了C#实现跨线程操作控件方法,主要采用异步访问方式实现,需要的朋友可以参考下...2020-06-25
  • C# WinForm多线程解决界面卡死问题的完美解决方案,使用BeginInvoke

    问题描述:当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决一个主线程来创建界...2020-06-24
  • C#开启线程的四种方式示例详解

    今天小编就为大家分享一篇关于C#开启线程的四种方式示例详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2020-06-25
  • 解决线程并发redisson使用遇到的坑

    这篇文章主要介绍了解决线程并发redisson使用遇到的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-18
  • 详解C++ bitset用法

    这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • php创建无限级树型菜单

    写递归函数,可考虑缓存,定义一些静态变量来存上一次运行的结果,多程序运行效率很有帮助.。 大概步骤如下: step1:到数据库取数据,放到一个数组, step2:把数据转化为一个树型状的数组, step3:把这个树型状的数组转为html代码。...2015-11-08
  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    这篇文章主要介绍了C#多线程编程中异步多线程的实现及线程池的使用,同时对多线程的一般概念及C#中的线程同步并发编程作了讲解,需要的朋友可以参考下...2020-06-25
  • Springboot实现多线程注入bean的工具类操作

    这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
  • Drupal模块开发之创建自己的钩子

    Drupal可以让第三方模块创建自己的钩子。在通常的实践中,有两种类型的钩子你可能想要创建,一种是内容修改类的钩子,一种是拦截类的钩子。 Drupal的钩子系统允许和模...2016-11-25
  • C++递归删除一个目录实例

    这篇文章主要介绍了C++递归删除一个目录的实现方法,涉及到目录的操作及递归算法的应用,需要的朋友可以参考下...2020-04-25
  • 区分c# 前台和后台线程

    这篇文章主要介绍了c# 前台线程和后台线程的区别与联系,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-09
  • JavaScript动态创建div属性和样式示例代码

    1.创建div元素: Javascript代码 复制代码 代码如下: <scripttypescripttype="text/javascript"> functioncreateElement(){ varcreateDiv=document.createElement("div"); createDiv.innerHTML="Testcreateadiveleme...2013-10-13
  • C++实现的O(n)复杂度内查找第K大数算法示例

    这篇文章主要介绍了C++实现的O(n)复杂度内查找第K大数算法,结合实例形式分析了算法的原理以及具体实现方法,需要的朋友可以参考下...2020-04-25
  • VSCode搭建C/C++编译环境的详细教程

    Visual Studio Code是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮、智能代码补全、自定义热键、括号匹配、代码片段、代码对比 Diff、GIT 等特性,这篇文章主要介绍了VSCode搭建C/C++编译环境,需要的朋友可以参考下...2020-05-15
  • C++ 将数据转为字符串的几种方法

    这篇文章主要介绍了C++ 将数据转为字符串的几种方法,十分的实用,有需要的小伙伴可以参考下。...2020-04-25
  • Windows配置VSCode+CMake+Ninja+Boost.Test的C++开发环境(教程详解)

    这篇文章主要介绍了Windows配置VSCode+CMake+Ninja+Boost.Test的C++开发环境,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-05-12
  • C#多线程中的异常处理操作示例

    这篇文章主要介绍了C#多线程中的异常处理操作,涉及C#多线程及异常的捕获、处理等相关操作技巧,需要的朋友可以参考下...2020-06-25
  • C#多线程与异步的区别详解

    多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别...2020-06-25