当前位置: 首页 > news >正文

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库中的其他控制函数

相关文章:

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

进程与线程 进程&#xff1a;直观的说就是任务管理器中各种正在运行的程序。对于操作系统来说&#xff0c;进程仅仅是一个数据结构&#xff0c;并不会真实的执行代码 线程&#xff1a;通常被称作但并不真的是轻量级进程或实际工作中的进程&#xff0c;它会真实的执行代码。每…...

【Qt】控件概述(2)—— 按钮类控件

控件概述&#xff08;2&#xff09; 1. PushButton2. RadioButton——单选按钮2.1 使用2.2 区分信号 clicked&#xff0c;clicked(bool)&#xff0c;pressed&#xff0c;released&#xff0c;toggled(bool)2.3 QButtonGroup分组 3. CheckBox——复选按钮 1. PushButton QPushB…...

Java访问器方法和更改器方法

一.访问器方法 1.访问器方法的定义和用途 访问器方法&#xff0c;通常也称为getter方法&#xff0c;是一种在面向对象编程中用于从类的外部访问私有字段值的特殊方法。这些方法的设计目的是为了提供对类内部状态的受限访问&#xff0c;同时保持类的封装性。通过使用访问器方法&…...

CAN协议帧结构

一、数据帧的整体结构 ┌───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐ │ SOF │ ID[11]│ RTR │ IDE │ DLC │ Data …...

valgrind 单例模式的自动释放(多线程)

单例模式&#xff0c;其中对象是由_pInstance指针来保存的&#xff0c;而在使用单例设计模式的过程中&#xff0c;也难免会遇到内存泄漏的问题。那么是否有一个方法&#xff0c;可以让对象自动释放&#xff0c;而不需要程序员自己手动去释放呢&#xff1f; ——嵌套类 5.1、内…...

OpenFegin

文章目录 一、OpenFegin是什么&#xff1f;二、基本使用三、超时重试机制4.自定义超时重传机制五、底层实现 一、OpenFegin是什么&#xff1f; OpenFeign的全称为Spring Cloud OpenFeign(下文简称OpenFeign),是Spring Cloud团队开发的一款基于 Feign的框架&#xff0c;声明式W…...

LeetCode-2608. 图中的最短环【广度优先搜索 图,腾讯面试真题】

LeetCode-2608. 图中的最短环【广度优先搜索 图&#xff0c;腾讯面试真题】 题目描述&#xff1a;解题思路一&#xff1a;【一图秒懂】枚举起点跑 BFS解题思路二&#xff1a;背诵版解题思路三&#xff1a; 题目描述&#xff1a; 现有一个含 n 个顶点的 双向 图&#xff0c;每个…...

IDEA 编译报错 “java: 常量字符串过长” 的解决办法

目录 一、问题描述二、问题原因2.1 理论角度2.2 源码角度 三、解决方案解决方案①&#xff1a;StringBuilder 拼接解决方案②&#xff1a;读取文件内容 四、方案验证 在线文本换行工具&#xff1a; https://lzltool.cn/Toolkit/WrapWordsInText 一、问题描述 今天在开发过程中…...

RK3568平台开发系列讲解(I2C篇)I2C 总线实现 client 设备方法

🚀返回专栏总目录 文章目录 一、非设备树实现 i2c client1.1、i2c_new_device1.2、i2c client二、设备树实现 i2c2.1、i2c_client 结构体的生成2.2、i2c_driver 驱动2.2.1、module_i2c_driver2.2.2、fan53555_regulator_probe沉淀、分享、成长,让自己和他人都能有所收获!�…...

K8S安装和部署

环境部署说明 主机IPmaster172.25.254.100node10172.25.254.10node20172.25.254.20harbor172.25.254.233 所有节点禁用selinux和防火墙 所有节点同步时间和解析 所有节点安装docker-ce 所有节点禁用swap&#xff0c;注意注释掉/etc/fstab文件中的定义 解析配置&#xff08;…...

Singleton(单例模式)

1. 意图 在开发中&#xff0c;若某些模块或功能只需要一个类实例&#xff0c;所有调用地方通过着一个类对象访问功能&#xff0c;单例模式符合这种类实例创建模式&#xff0c;并且通过提供统一类实例接口访问类对象。 2. 适用性 《Gof 设计模式-可复用面向对象软件的基础》中对…...

【Linux报错】“-bash: cd: too many arguments“

问题描述 今天使用 cd 想要调整某个文件目录时&#xff0c;发现以下报错 原因分析&#xff1a; arguments 是参数的意思&#xff0c;该报错提示参数过多&#xff0c;意味着系统识别到了多余参数 本质原因&#xff1a;你的命令中输入了多余的 ”空格“ &#xff0c;检查一…...

C# WebService返回参数为DataTable报错“XML文档有错误”

该问题由于DataTable列存在自定义类型。 解决该报错需要以下几步&#xff1a; 1、自定义类型增加xml序列化 2、由于C#从 XML 反序列化 DataSet 或 DataTable 时的默认限制&#xff0c;所以需要先把调用方的项目开放限制&#xff0c;如果是.netframework项目&#xff0c;需要…...

[paddle]paddleseg快速开始

快速开始 为了让大家快速了解PaddleSeg&#xff0c;本文档使用一个简单示例进行演示。在实际业务中&#xff0c;建议大家根据实际情况进行调整适配。 在开始下面示例之前&#xff0c;请大家确保已经安装好PaddleSeg开发环境&#xff08;安装说明&#xff09;。 1 准备数据 …...

UNIAPP popper气泡弹层【unibest框架下】vue3+typescript

看了下市场的代码&#xff0c;要么写的不怎么好&#xff0c;要么过于复杂。于是把市场的代码下下来了自己改。200行代码撸了个弹出层组件。兼容H5和APP。 功能&#xff1a; 1)只支持上下左右4个方向的弹层不支持侧边靠齐 2)不对屏幕边界适配 3)支持弹层外边点击自动隐藏 4)支持…...

launcher.py: error: the following arguments are required: --output_dir

记录一个LLaMA-Factroy配置过程。 安装 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e ".[torch,metrics]"训练 CUDA_VISIBLE_DEVICES0 llamafactory-cli train example/train_lora/.yaml按理说配置好文件应…...

C语言基础之结构体

今天我们来讲讲C语言基础的最后一个知识点了 —— 结构体。不知道大家对前面的C语言基础的知识点掌握的怎么样了呢&#xff1f;下面我们就开始讲解结构体的相关知识点吧&#xff01; 什么是结构体呢&#xff1f;或者说结构体有什么作用呢&#xff1f;对于复杂对象来说&#xff…...

Redis入门第四步:Redis发布与订阅

欢迎继续跟随《Redis新手指南&#xff1a;从入门到精通》专栏的步伐&#xff01;在本文中&#xff0c;我们将深入探讨Redis的发布与订阅&#xff08;Pub/Sub&#xff09;模式。这是一种强大的消息传递机制&#xff0c;适用于各种实时通信场景&#xff0c;如聊天应用、实时通知和…...

MySQL 之权限与授权

MySQL 权限及授权系统用于控制数据库用户对数据库资源的访问和操作权限。它提供了一种细粒度的安全控制机制&#xff0c;确保只有被授权的用户才能执行特定的操作。MySQL 的权限控制体系非常灵活&#xff0c;支持多种权限类型及级别&#xff08;数据库、表、列、存储过程等&…...

解决方案:Pandas里面的loc跟iloc,有什么区别

文章目录 一、现象二、解决方案案例使用loc使用iloc 简单总结 一、现象 在用Pandas库处理数据的时候&#xff0c;久而久之不用loc跟iloc&#xff0c;难免会有些混乱记混 二、解决方案 在Pandas中&#xff0c;loc和iloc是两种常用的数据选择方法&#xff0c;它们的主要区别在…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...

土建施工员考试:建筑施工技术重点知识有哪些?

《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目&#xff0c;核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容&#xff0c;附学习方向和应试技巧&#xff1a; 一、施工组织与进度管理 核心目标&#xff1a; 规…...