Windows系统编程(三)进程与线程二

news2024/10/6 22:47:58

进程与线程

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

线程:通常被称作但并不真的是轻量级进程或实际工作中的进程,它会真实的执行代码。每个线程都有一个需要执行的代码块称为线程回调函数。每个进程启动的时候会同步启动一个主线程,而主线程所执行的代码块就是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.coloradmin.cn/o/2193097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强制删除了windows自带的edge浏览器,重装不了怎么办【已解决】

#最近我的edge浏览器出了点问题&#xff0c;点击打不开但是能在下面的任务栏看到他开启了&#xff0c;就是不能够显示在桌面&#xff0c;小窗口叫我配置设置。 我不懂&#xff0c;感觉很烦&#xff0c;就把他强制卸载了。但是windows是不允许将他卸载的&#xff0c;使用window…

LeetCode讲解篇之695. 岛屿的最大面积

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们遍历二维矩阵&#xff0c;如果当前格子的元素为1进行深度优先搜索&#xff0c;将搜索过的格子置为0&#xff0c;防止重复访问&#xff0c;然后对继续深度优先搜索上下左右中为1的格子 题解代码 func maxAr…

sublime配置(竞赛向)

我也想要有jiangly一样的sublime 先决条件 首先&#xff0c;到官网上下载最新的sublime4&#xff0c;然后在mingw官网上下载最新的mingw64 mingw64官网&#xff1a;左边菜单栏点击dowloads,然后选择MinGW-W64-builds(可能会有点慢)——然后有时候会变成选LLVM-minGW,接着选择…

Vue入门-指令学习-v-show和v-if

v-show&#xff1a; 作用&#xff1a;控制元素的显示隐藏 语法&#xff1a;v-show"表达式" 表达式值true显示&#xff0c;false隐藏 v-if 作用&#xff1a;控制元素的显示隐藏&#xff08;条件渲染&#xff09; 语法&#xff1a; vif"表达式" 表达式tr…

【Mybatis篇】Mybatis的注解开发

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】&#xff0c;【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 文章目录 &#x1f3af; Select注解 …

Nuxt.js 应用中的 app:suspense:resolve 钩子详解

title: Nuxt.js 应用中的 app:suspense:resolve 钩子详解 date: 2024/10/6 updated: 2024/10/6 author: cmdragon excerpt: app:suspense:resolve 是一个强大的钩子,允许开发者在异步数据解析完成后的最后一步执行必要的处理。通过合理使用该钩子,我们可以优化组件的渲染…

AI赋能,旅游新纪元,看旅游大厂携程的AI实践

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 国庆假期马上过去了&#xff0c;2024年国庆…

智能制造的人机料法环的内涵

在生产和管理领域,有个很重要的概念叫 “人、机、料、法、环”。 “人” 就是参与其中的人员,他们的技能、态度、责任心等对事情的结果影响很大; “机” 指的是机器设备和工具等,就像干活要用的家伙事儿,好不好用、正不正常直接关系到工作的效率和质量; “料” 呢,就…

【动态规划-最长公共子序列(LCS)】【hard】【科大讯飞笔试最后一题】力扣115. 不同的子序列

给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 10^9 7 取模。 示例 1&#xff1a; 输入&#xff1a;s “rabbbit”, t “rabbit” 输出&#xff1a;3 解释&#xff1a; 如下所示, 有 3 种可以从 s 中得到 “rabbit”…

手撕正弦-余弦位置编码(Sinusoidal Positional Encoding)

改写后的代码&#xff1a; import torch import math import torch.nn as nnclass PositionalEncoder(nn.Module):def __init__(self, d_model, max_seq_len80):super().__init__()self.d_model d_model# 根据 pos 和 i 创建一个常量 PE 矩阵pe torch.zeros(max_seq_len, d_…

【unity游戏开发】彻底理解AnimatorStateInfo,获取真实动画长度

前言 前置知识&#xff1a;设置参数后&#xff0c;下一个循环才会切换对应动画&#xff0c;所以在下一个循环获取真实的动画长度 AnimatorStateInfo是结构体&#xff01;值类型&#xff0c;要不断重复获取才是最新的 主要是自动设置trigger切换的动画自动切回上一个动画&#x…

Linux命令:用于管理 Linux 系统中用户组的命令行工具gpasswd详解

目录 一、概述 二、使用方法 1、 基本语法 2、 常用选项 &#xff08;1&#xff09;无选项&#xff08;仅组名&#xff09; &#xff08;2&#xff09;-a 用户 &#xff08;3&#xff09;-d 用户 &#xff08;4&#xff09;-M 用户列表 &#xff08;5&#xff09;-A 管…

系统架构设计师-下午案例题(2021年下半年)

1.试题一(共25分) 阅读以下关于软件架构设计与评估的叙述在答题纸上回答问题1和问题2。 【说明】某公司拟开发一套机器学习应用开发平台支持用户使用浏览器在线进行基于机器学习的智能应用开发活动。该平台的核心应用场景是用户通过拖拽算法组件灵活定义机器学习流程&#xf…

【Linux】信号知识三把斧——信号的产生、保存和处理

目录​​​​​​​ 1、关于信号的前置知识 1.1.什么是信号&#xff1f; 1.2.为什么要学习信号&#xff1f; 1.3.如何学习信号&#xff1f; 1.4.一些常见的信号 1.5.信号的处理方式 1.6.为什么每一个进程都可以系统调用&#xff1f; 2.信号的产生 2.1.kill命令产生信号…

[Python学习日记-40] 函数进阶之装饰器

[Python学习日记-40] 函数进阶之装饰器 简介 引子 什么是装饰器 装饰器终结版 装饰器的层层叠加 简介 在前面铺垫了这么多终于该讲到重点了&#xff0c;前面说的匿名函数、高阶函数、闭包等等都是为了这篇文章所讲的装饰器而使用的&#xff0c;本篇文章将会一一个故事通俗…

个人网站,怎么操作才能提升个人网站的流量

运营个人网站以提升流量是一个综合性的过程&#xff0c;涉及内容优化、技术调整、用户体验提升以及外部推广等多个方面。以下是一些专业建议&#xff0c;旨在帮助个人网站运营者有效提升网站流量&#xff1a; 1.精准关键词研究与优化 -关键词研究&#xff1a;利用工具如谷歌…

关于 Python 3.13 你所需要知道的几点

什么是全局解释器锁 (GIL)&#xff1f; 自20世纪80年代末&#xff0c;Guido Van Rossum在荷兰阿姆斯特丹东部的一个科技园区开始开发Python编程语言&#xff0c;它最初被设计为一种单线程的解释型语言。这到底是什么意思呢&#xff1f; 你可能会听说&#xff0c;编程语言分为解…

C++-容器适配器- stack、queue、priority_queue和仿函数

目录 1.什么是适配器 2.deque 1.简单了解结构 2.deque的缺陷 3.为什么选择deque作为stack和queue的底层默认容器 3.stack&#xff08;栈&#xff09; 4.queue&#xff08;队列&#xff09; 5.仿函数 6.priority_queue&#xff08;优先级队列&#xff09;&#xff08;堆…

IDEA 2024.3 预览:把开发者感动到哭了

幸运的人&#xff0c; 一生都被童年治愈&#xff1b; 不幸的人&#xff0c; 一生都在治愈童年 只有勇敢的人 和有钱的人才能先享受世界 缘分就是我还不知道 会见到你就误打误撞般 遇见了你 最近 IDEA 又发布了最新的 2024.3 的预览版本 EAP&#xff0c;把开发者的心激动的…

躺平成长-第四周的开发日记

回顾自己的小程序&#xff0c;现在自己有饮食/跑步/养生操/学习/学历提升/等多样化的kp值计算公式页面&#xff0c;自己tarbar导航页面有两个内容&#xff0c;kp值计算&#xff0c;躺平显示&#xff0c;单纯这些功能自己使用下来&#xff0c;会感到一种疲惫&#xff01;&#x…