Windows系统编程(三)线程并发

进程与线程

进程:直观的说就是任务管理器中各种正在运行的程序。对于操作系统来说,进程仅仅是一个数据结构,并不会真实的执行代码

线程:通常被称作但并不真的是轻量级进程或实际工作中的进程,它会真实的执行代码。每个线程都有一个需要执行的代码块称为线程回调函数。每个进程启动的时候会同步启动一个主线程,而主线程所执行的代码块就是main函数。当main函数结束时,主线程结束并销毁,同时其他子线程随之销毁

真并发与伪并发

伪并发

在早期的cpu即单核cpu中,因性能核心各方面较为落后,并发编程实际是一个伪并发编程,即系统中所有进程按照优先级去抢占cpu时间片,也就是系统一会执行这个一会执行哪个。

由于抢占时间片所需时间较短,所以我们并不觉得程序卡顿。但各进程抢占cup时间片是一个很麻烦的事情,cpu虽然提供任务切换的功能即TSS任务段,但Windows并不使用。这是因为Windows自己实现了线程调度,即在线程切换时,上个线程代码执行到的地方的线程的状态,线程上下文,通用寄存器,段寄存器,硬件调试寄存器,EIP(指令指针寄存器),EFLAGS等都会被Windows通过Windows(Context)保存,直到再次切换回来后再加载

真并发

随着科技的发展,cpu由单核cpu变成了多核cpu。此时多个核心可以同时独立执行一个 任务,此时也称作真并发

并发形式

1.多进程并发:一个进程里只有一个线程,同时启动多个进程实现并发,如浏览器打开的多个窗口

2.多线程并发:一个进程内运行多个线程,是真实的并发。其中存在变量的访问问题,具体如下:有Value = 100 全局变量以及A,B两个线程。初始时A,B线程访问Value,访问值都是100,现AB两线程都对Value进行++。但操作完成后,Value的值为101,丢失了一个操作。这种情况叫做线程同步问题

线程的生存周期

1.当该线程回调函数执行完毕时,自然死亡

2.当主线程死亡时,子线程被动死亡

并发与并行:并发更强调数量,并行更强调性能

线程应用

普通函数应用

#include<iostream>
#include<thread>
void FirstThreadCallBack() //构建一个普通函数作为子线程
{for (size_t i = 0; i < 100000; i++){std::cout << "First:" << i << std::endl;}
}
int main()
{std::thread obj(FirstThreadCallBack); //声明线程对象,启动一个线程去执行线程回调函数for (size_t i = 0; i < 100000; i++){std::cout << "main:"<< i << std::endl;}system(“pause”);//加上此函数使主线程不会结束,让我们更清晰看到线程并发的过程。否则主线程结束子线程随之结束return 0;
}

此时程序会同时进行上述两个循环打印

仿函数应用

#include<iostream>
#include<thread>
class Exec//一个仿函数
{
public:void operator()()const{std::cout << "Exec" << std::endl;}
};
int main()
{Exec e;std::thread obj(e);return 0;
}

此时打印Exec

Lambda应用

#include<iostream>
#include<thread>
int main()
{std::thread obj([] {std::cout << "Lambda" << std::endl; });return 0;
}

此时程打印Lambda

综上可知,任何可以调用的类型都可以用于线程对象的构造函数传参

线程死亡

一旦线程启动了,我们就需要知道线程是怎么结束的

1.自然死亡:thread析构函数terminate()在子线程执行完毕后析构子线程

2.非自然死亡:thread析构函数执行完毕时,子线程析构,但子线程并没有执行完毕

3.等待:绝对的自然死亡 等待子线程执行完毕后,程序再进行执行

4.不再等待:主线程存活时后台运行,依赖于主线程的存活

5.如果一个线程是Windows原生线程,主线程销毁后其也会死亡

Windows原生线程

现在我们验证一下,当主线程死亡时,Windows原生线程会不会死亡

#include<iostream>
#include<thread>
#include<windows.h>
DWORD ThreadCallBack(LPVOID lpThreadParameter)
{for (size_t i = 0; i < 100000; i++){std::cout << "First:" << i << std::endl;}return 0;
}
int main()
{CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ThreadCallBack, NULL, NULL, NULL);//创建了一个windows原生线程return 0;
}

此时运行程序,发现随着主线程的结束该Windows原生线程死亡

等待死亡

#include<iostream>
#include<thread>
#include<windows.h>
DWORD ThreadCallBack(LPVOID lpThreadParameter)
{for (size_t i = 0; i < 100000; i++){std::cout << "First:" << i << std::endl;}return 0;
}
int main()
{HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ThreadCallBack, NULL, NULL, NULL);//创建一个原生的Windows线程WaitForSingleObject(hThread, -1); //此时主线程会永久等待该子线程结束以后再结束return 0;
}

此时运行程序原生线程不会死亡,直到它运行完毕

阻塞等待

#include<iostream>
#include<thread>
#include<windows.h>
DWORD ThreadCallBack(LPVOID lpThreadParameter)
{for (size_t i = 0; i < 100000; i++){std::cout << "First:" << i << std::endl;}return 0;
}
int main()
{std::thread obj(FirstThreadCallBack);//创建一个普通的线程obj.join(); //阻塞等待,作用是在此处等待子线程结束,程序再继续运行。//当使用此函数时,我们通常需要加一个异常处理。这是因为子线程可能会出现一个异常报错而导致无法执行完毕以至于程序一直处于阻塞等待的情况return 0;
}

此时运行程序,知道子线程运行完毕,主线程才会结束

不再等待

#include<iostream>
#include<thread>
#include<windows.h>
DWORD ThreadCallBack(LPVOID lpThreadParameter)
{for (size_t i = 0; i < 100000; i++){std::cout << "First:" << i << std::endl;}return 0;
}
int main()
{std::thread obj(FirstThreadCallBack);obj.detach(); //不再等待:同Windows原生线程一样,主线程死亡,其子线程也死亡 
//此时额外加一个循环,程序在执行该循环时,主线程没有死亡,子线程也不会死亡,而是一起执行两个线程for (size_t i = 0; i < 100000; i++) {std::cout << "main:" << i << std::endl;}return 0;
}

线程同步问题

问题演示

如下当我们演示一个简单的线程同步

#include<iostream>
#include<thread>
#include<windows.h>
#include<string.h>
void Print(std::string szBuffer,int nCount)
{for (size_t i = 0; i < nCount; i++){std::cout << szBuffer << ":" << i << std::endl;}
}
int main()
{std::thread obj(Print,"abc",200);system(“pause”);return 0;
}

程序运行发现:

原因:这就是时间切片的伪并发可能出现的问题,很形象展示了线程同步问题这个现象

现我们针对如下线程同步程序进行进一步的问题解决讲解

#include <iostream>
#include <thread>
int g_Value = 0; 
void add()
{for (size_t i = 0; i < 1000000; i++){g_Value++;}
}
int main()
{std::thread objA(add);std::thread objB(add);objA.join();objB.join();std::cout << g_Value << std::endl;system("pause");return 0;
}

程序运行以后,g_Value的最终结果应该是2000000,但但每次运行时g_Value都是随机数,这是因为在线程同步时出现丢失操作

互斥体解决线程同步问题

方法一:使用互斥体方法

#include <iostream>
#include <thread>
#include<mutex>
int g_Value = 0; 
std::mutex some_mutex; //声明一个互斥体,用于线程可能出错的地方
void add()
{for (size_t i = 0; i < 1000000; i++){some_mutex.lock(); //该函数被互斥体加锁保护。当一个线程在访问该函数时,其他线程无法访问g_Value++; some_mutex.unlocke(); //互斥体解锁}
}//此时该函数不会再出现多线程同时访问的问题了
int main()
{std::thread objA(add);std::thread objB(add);objA.join();objB.join();std::cout << g_Value << std::endl;system("pause");return 0;
}

方法二:使用锁类模板

#include <iostream>
#include <thread>
#include<mutex>
int g_Value = 0; 
void add()
{for (size_t i = 0; i < 1000000; i++){//构造函数调用时加锁,析构函数调用时解锁std::lock_guard<std::mutex> guard(some_mutex); g_Value++;}
}
int main()
{std::thread objA(add);std::thread objB(add);objA.join();objB.join();std::cout << g_Value << std::endl;system("pause");return 0;
}

以上两种方法可以很好的解决线程同步问题

作业

01.尝试使用多线程造成线程同步问题。
02.尝试使用thread库中的其他控制函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/442742.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qwen变体新成员加一,英伟达训练 NVLM-D-72B 视觉大模型

今天&#xff08;2024 年 9 月 17 日&#xff09;&#xff0c;我们推出了前沿级多模态大语言模型&#xff08;LLM&#xff09;系列 NVLM 1.0&#xff0c;它在视觉语言任务上取得了最先进的结果&#xff0c;可与领先的专有模型&#xff08;如 GPT-4o&#xff09;和开放存取模型&…

易图讯军用VR三维电子沙盘系统

深圳易图讯军用VR三维电子沙盘系统是一种集成了虚拟现实&#xff08;VR&#xff09;技术、三维建模技术、大数据分析、实时动态更新以及高度安全可靠的综合性军事指挥平台。该系统通过高精度三维模型真实再现战场环境&#xff0c;为指挥员提供沉浸式体验和交互操作的可能性&…

【黑马点评】0.环境配置--Redis6.2.6和可视化工具在Windows上的安装

黑马点评--0.Redis6.2.6在windows上的环境配置与可视化 0 前言1 下载安装2 解压后运行msi文件3 修改配置文件并打开Redis3.1 修改密码&#xff08;可选&#xff09;3.2 测试 4 Redis可视化&#xff08;可选&#xff09;4.1 Another Redis Desktop Manager下载安装4.2 连接Redis…

N1从安卓盒子刷成armbian

Release Armbian_noble_save_2024.10 ophub/amlogic-s9xxx-armbian (github.com) armbian下载&#xff0c;这里要选择905d adb 下载地址 https://dl.google.com/android/repository/platform-tools-latest-windows.zip 提示信息 恩山无线论坛 使用usb image tool restet a…

深入理解NumPy库:常用函数详解与数组操作指南

在数据科学和数值计算领域&#xff0c;NumPy无疑是一个强大的工具&#xff0c;它为Python提供了高效的多维数 组处理能力。无论是进行数据分析、构建机器学习模型&#xff0c;还是进行复杂的科学计算&#xff0c;NumPy都是 不可或缺的核心库之一。 numpy.array 是 NumPy 库中…

C# 获取可执行文件目录

---------------------------------------------------------------------------

SpringMVC框架:入门讲解和基础案例解析

Spring Web MVC是什么&#xff1f; Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架。使用了MVC架构模式的思想&#xff0c;将web层进行职责解耦&#xff0c;基于请求驱动指的就是使用请求-响应模型 。框架的目的就是帮助我们简化开发&…

PCB板材基本知识

术语 名称定义插图 copper foil 铜箔 Copper Clad Laminates&#xff0c;CCL 覆铜箔层压板 CCL是PCB制造的上游核心材料&#xff0c;是将电子玻纤布或其它增强材料浸以树脂&#xff0c;一面或双面覆以铜箔并经热压而制成的一种板状材料&#xff0c;担负着&#xff08;PCB&am…

优先级队列详解

一&#xff0c;优先级队列 什么是优先级队列呢&#xff0c;不知道大家了解过队列没有&#xff0c;队列是一种先进先出的数据结构&#xff0c;但是我们有时会想让优先级高的先出队列&#xff0c;所以我们出现了一种新的数据结构&#xff0c;我们实现两种主要功能得到优先级高的数…

螺蛳壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习07(基于docker容器的防火墙及NAT企业实战)

7.1 网络准备 7.2 网络规划 1&#xff09;虚拟网络编辑器 点击右下方“更改设置”&#xff0c;点击“添加网络”假如vmnet3和vmnet4&#xff0c;然后分别选择vmnet3和vmnet4&#xff0c;设置为“仅主机模式”&#xff0c;按③处处理&#xff0c;去掉“使用DHCP”&#xff0c;…

ORA-19815 db_recovery_file_dest_size 100%

1、alert日志报错 ORA-19815 db_recovery_file_dest_size 100% 恢复区空间使用满 2、rm删除后操作系统空间使用&#xff0c;但V$RECOVERY_FILE_DEST记录的空间使用率仍然是满的 3、rman delete expired 归档日志后恢复正常 4、当然可以通过增大db_recovery_file_dest_size来临时…

牛客——xay loves or与 __builtin_popcount的使用

xay loves or 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行思路 题目要求我们计算有多少个正整数 yy 满足条件 x \text{ OR } y sx OR ys。这里的“OR”是指按位或运算。为了理解这个问题&#xff0c;我们需要考虑按位或运算的性质。 对于任意两个位 a_iai​ 和 b_…

HUAWEI_HCIA_实验指南_Lib1.4_配置通过Telnet登录系统

一、原理概述 Telnet(Telecommunication Network Protocol)起源于ARPANET,是最早的Internet应用之一。 Telnet 通常用在远程登录应用中&#xff0c;以便对本地或远端运行的网络设备进行配置、监控和维护。如网络中有多台设备需要配置和管理&#xff0c;用户无需为每一台设备…

NUKE 15有哪些新的改进功能?影视后期特效合成NUKE 15 安装包分享 【Mac/win】

Nuke 15是一款由英国The Foundry公司开发的专业的合成软件&#xff0c;被广泛用于电影、电视和广告制作中的后期合成和特效制作。 Nuke 15拥有强大的功能和灵活性&#xff0c;可以帮助用户处理各种复杂的合成任务&#xff0c;包括图像修复、色彩校正以及粒子特效等。它具备高效…

Java项目实战II基于Java+Spring Boot+MySQL的高校学科竞赛平台

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着高等教…

【JavaScript】JS核心语法及函数

文章目录 一、初识 JS二、JS 核心语法2-1 变量2-2 数据类型typeofString 对象 2-3 数组创建数组常用属性方法 2-4 运算符号加号运算符 减号运算符 -比较运算符逻辑运算符 2-5 控制语句for-inbreakcontinue 三、函数3-1 常用系统函数3-2 自定义函数函数声明函数调用 3-3 创建对象…

家里养有宠物应该用哪款宠物空气净化器比较好?哪款最能吸毛?

这不是国庆节刚过吗&#xff0c;我的小猫终于是平安的度过了在农村生活的时光&#xff0c;之前还担心会不会被爸妈嫌弃&#xff0c;这下好了&#xff0c;嫌弃也过了国庆节。 但是一把猫咪带回出租房&#xff0c;由于几天不在房子里待&#xff0c;猫咪对熟悉的环境又特别激动&a…

C语言贪吃蛇

#只讲逻辑不讲一些基础&#xff0c;基础大概过一遍就行# project-one: 无 (gitee.com)仓库里面有原代码 一、基础工作 1、先将你的编译器换成32位环境&#xff0c;也就是x86&#xff0c; 如果是控制台主机窗口则管&#xff0c;若不是需要改为控制台主机窗口 打开运行窗口后点…

构建宠物咖啡馆:SpringBoot框架的实现策略

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于Spring Boot的宠物咖啡馆平台的设计与…

Authentication Lab | IP Based Auth Bypass

关注这个靶场的其它相关笔记&#xff1a;Authentication Lab —— 靶场笔记合集-CSDN博客 0x01&#xff1a;IP Based Auth Bypass 前情提要 有些开发人员为了图方便&#xff0c;会给站点设置一个 IP 白名单&#xff0c;如果访问站点的用户的 IP 在白名单内&#xff0c;则允许访…