多线程(2):线程同步

news2024/11/15 5:32:37

线程同步是在多线程编程过程中对数据保护的一种机制,保护的数据是共享数据。共享数据就是多个线程共同访问的一块资源,也就是一块内存。假设有3个线程,其中A,B线程在同一个时间点往这块内存中写数据,于此同时C线程往这块内存中读数据,注意事同一个时间点,如果不是一个时间点就没有线程同步的问题了,很显然,同一时间点读写数据,读出的数据很可能就出现问题,不是正确的数据。

假设有 4 个线程 A、B、C、D,当前一个线程 A 对内存中的共享资源进行访问的时候,其他线程 B, C, D 都不可以对这块内存进行操作,直到线程 A 对这块内存访问完毕为止,B,C,D 中的一个才能访问这块内存,剩余的两个需要继续阻塞等待,以此类推,直至所有的线程都对这块内存操作完毕。 线程对内存的这种访问方式就称之为线程同步,通过对概念的介绍,我们可以了解到所谓的同步并不是多个线程同时对内存进行访问,而是按照先后顺序依次进行的。也就是说线程同步,并不是让线程并行去执行,而是让线程串行去执行。

因此线程同步访问共享资源的时候,由于是串行的,所以效率肯定就变低了。但线程同步可以保证共享数据的安全性
·

1.1 为什么要同步

在研究线程同步之前,先来看一个两个线程同时数数(每个线程数 50 个数,交替数到 100)的例子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>

#define MAX 50
// 全局变量
int number;

// 线程处理函数
void* funcA_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        int cur = number;
        cur++;
        usleep(10);
        number = cur;
        printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
    }

    return NULL;
}

void* funcB_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        int cur = number;
        cur++;
        number = cur;
        printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
        usleep(5);
    }

    return NULL;
}

int main(int argc, const char* argv[])
{
    pthread_t p1, p2;

    // 创建两个子线程
    pthread_create(&p1, NULL, funcA_num, NULL);
    pthread_create(&p2, NULL, funcB_num, NULL);

    // 阻塞,资源回收
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);

    return 0;
}

上面的例子是两个线程进行同时数数

  • 在main函数中,创建了2个子线程p1,p2, 另外还有一个主线程,也就是总共3个线程。两个子线程分别执行任务函数funcA_numfuncB_num。并通过pthread_join阻塞等待子线程执行完成并回收资源。
  • funcA_num定义了一个for循环,执行MAX也就是数Max=50个数,输一次打印一次结果; 在funcB_num中也是做了相同的操作,数了50个数。
  • 数数的时候都是基于全局变量number进行数据递增,很显然两个线程各自数50个,并操作同一个共享全局变量,最终应该得到100.

编译并执行上面的测试程序,得到如下结果:

$ ./a.out 
Thread B, id = 140504473724672, number = 1
Thread B, id = 140504473724672, number = 2
Thread A, id = 140504482117376, number = 2
Thread B, id = 140504473724672, number = 3
Thread A, id = 140504482117376, number = 4
Thread B, id = 140504473724672, number = 5
Thread A, id = 140504482117376, number = 6
Thread B, id = 140504473724672, number = 7
Thread B, id = 140504473724672, number = 8
Thread A, id = 140504482117376, number = 7
Thread B, id = 140504473724672, number = 8
Thread B, id = 140504473724672, number = 9
Thread A, id = 140504482117376, number = 8
Thread B, id = 140504473724672, number = 9
Thread A, id = 140504482117376, number = 9
Thread B, id = 140504473724672, number = 10
Thread B, id = 140504473724672, number = 11
Thread A, id = 140504482117376, number = 10
Thread B, id = 140504473724672, number = 11
Thread A, id = 140504482117376, number = 11
Thread B, id = 140504473724672, number = 12
Thread A, id = 140504482117376, number = 13
Thread B, id = 140504473724672, number = 14
Thread A, id = 140504482117376, number = 15
Thread B, id = 140504473724672, number = 16
Thread B, id = 140504473724672, number = 17
Thread B, id = 140504473724672, number = 18
Thread B, id = 140504473724672, number = 19
Thread A, id = 140504482117376, number = 17
Thread B, id = 140504473724672, number = 18
Thread B, id = 140504473724672, number = 19
Thread A, id = 140504482117376, number = 19
Thread B, id = 140504473724672, number = 20
Thread A, id = 140504482117376, number = 20
Thread B, id = 140504473724672, number = 21
Thread A, id = 140504482117376, number = 21
Thread B, id = 140504473724672, number = 22
Thread A, id = 140504482117376, number = 22
Thread B, id = 140504473724672, number = 23
Thread A, id = 140504482117376, number = 23
Thread B, id = 140504473724672, number = 24
Thread A, id = 140504482117376, number = 24
Thread B, id = 140504473724672, number = 25
Thread A, id = 140504482117376, number = 25
Thread B, id = 140504473724672, number = 26
Thread A, id = 140504482117376, number = 26
Thread B, id = 140504473724672, number = 27
Thread A, id = 140504482117376, number = 27
Thread B, id = 140504473724672, number = 28
Thread A, id = 140504482117376, number = 28
Thread B, id = 140504473724672, number = 29
Thread A, id = 140504482117376, number = 29
Thread B, id = 140504473724672, number = 30
Thread A, id = 140504482117376, number = 30
Thread B, id = 140504473724672, number = 31
Thread A, id = 140504482117376, number = 31
Thread B, id = 140504473724672, number = 32
Thread A, id = 140504482117376, number = 32
Thread B, id = 140504473724672, number = 33
Thread A, id = 140504482117376, number = 33
Thread B, id = 140504473724672, number = 34
Thread A, id = 140504482117376, number = 34
Thread B, id = 140504473724672, number = 35
Thread A, id = 140504482117376, number = 35
Thread B, id = 140504473724672, number = 36
Thread A, id = 140504482117376, number = 36
Thread B, id = 140504473724672, number = 37
Thread A, id = 140504482117376, number = 37
Thread B, id = 140504473724672, number = 38
Thread A, id = 140504482117376, number = 38
Thread B, id = 140504473724672, number = 39
Thread A, id = 140504482117376, number = 39
Thread A, id = 140504482117376, number = 40
Thread B, id = 140504473724672, number = 41
Thread B, id = 140504473724672, number = 42
Thread A, id = 140504482117376, number = 42
Thread A, id = 140504482117376, number = 43
Thread B, id = 140504473724672, number = 44
Thread B, id = 140504473724672, number = 45
Thread A, id = 140504482117376, number = 45
Thread B, id = 140504473724672, number = 46
Thread A, id = 140504482117376, number = 46
Thread B, id = 140504473724672, number = 47
Thread A, id = 140504482117376, number = 47
Thread B, id = 140504473724672, number = 48
Thread A, id = 140504482117376, number = 48
Thread B, id = 140504473724672, number = 49
Thread A, id = 140504482117376, number = 50
Thread B, id = 140504473724672, number = 51
Thread A, id = 140504482117376, number = 51
Thread B, id = 140504473724672, number = 52
Thread A, id = 140504482117376, number = 53
Thread A, id = 140504482117376, number = 54
Thread A, id = 140504482117376, number = 55
Thread A, id = 140504482117376, number = 56
Thread A, id = 140504482117376, number = 57
Thread A, id = 140504482117376, number = 58
Thread A, id = 140504482117376, number = 59
Thread A, id = 140504482117376, number = 60
Thread A, id = 140504482117376, number = 61

通过对上面例子的测试,可以看出虽然每个线程内部循环了 50 次每次数一个数,但是最终没有数到 100,通过输出的结果可以看到,有些数字被重复数了多次,其原因就是没有对线程进行同步处理,造成了数据的混乱。

两个线程在数数的时候需要分时复用 CPU 时间片,并且测试程序中调用了 sleep() 导致线程的 CPU 时间片没用完就被迫挂起了,这样就能让 CPU 的上下文切换(保存当前状态,下一次继续运行的时候需要加载保存的状态)更加频繁,更容易再现数据混乱的这个现象。

在这里插入图片描述
CPU 对应寄存器、一级缓存、二级缓存、三级缓存是独占的,用于存储处理的数据和线程的状态信息,数据被 CPU 处理完成需要再次被写入到物理内存中,物理内存数据也可以通过文件 IO 操作写入到磁盘中。

在测试程序中两个线程共用全局变量 number 当线程变成运行态之后开始数数,从物理内存加载数据,然后将数据放到 CPU 进行运算,最后将结果更新到物理内存中。如果数数的两个线程都可以顺利完成这个流程,那么得到的结果肯定是正确的。

如果线程 A 执行这个过程期间就失去了 CPU 时间片,线程 A 被挂起了最新的数据没能更新到物理内存。线程 B 变成运行态之后从物理内存读数据,很显然它没有拿到最新数据,只能基于旧的数据往后数,然后失去 CPU 时间片挂起。线程 A 得到 CPU 时间片变成运行态,第一件事儿就是将上次没更新到内存的数据更新到内存,但是这样会导致线程 B 已经更新到内存的数据被覆盖,活儿白干了,最终导致有些数据会被重复数很多次

如何解决这一现象,其实就可以利用线程同步来解决。线程同步解决了数据没被写回去的问题,如果做了线程同步,就能保证数据从cpu同步到物理内存中。同步回去之后,另外一个线程才能去读取处理共享数据。

1.2 同步方式

对于多个线程访问共享资源出现数据混乱的问题,需要进行线程同步。常用的线程同步方式有四种互斥锁读写锁条件变量信号量。所谓的共享资源就是多个线程共同访问的变量,这些变量通常为全局数据区变量或者堆区变量,这些变量对应的共享资源也被称之为临界资源

在这里插入图片描述
找到临界资源之后,再找和临界资源相关的上下文代码,这样就得到了一个代码块,这个代码块可以称之为临界区。确定好临界区(临界区越小越好)之后,就可以进行线程同步了,线程同步的大致处理思路是这样的:

  • 在临界区代码的上边,添加加锁函数,对临界区加锁。
    • 哪个线程调用这句代码,就会把这把锁锁上(获得cpu时间片),其他线程就只能阻塞并失去了cpu时间片。
  • 临界区代码的下边,添加解锁函数,对临界区解锁。
    • 此时堵塞在这把锁的线程就被唤醒,然后抢占时间片,进入临界区。
    • 出临界区的线程会将锁定的那把锁打开,其他抢到锁的线程就可以进入到临界区了。
  • 通过锁机制能保证临界区代码最多只能同时有一个线程访问,这样并行访问就变为串行访问了

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

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

相关文章

视频编码流程 YUV数据编码为H264数据

文章目录 1.视频编码流程2.实战demo3.相关编码知识点讲解1. 参数设置问题:2. 关于av_opt_set3. 关于码流设置 1.视频编码流程 2.实战demo #ifndef MAINBACK_C #define MAINBACK_C #endif // MAINBACK_C #include <stdint.h> #include <stdio.h> #include <stdl…

火车头采集器AI伪原创[php源码]

本文介绍php版本的火车头采集器AI伪原创&#xff0c;对于网站的原创内容&#xff0c;站长朋友们一定很头疼。作为一个草根站长&#xff0c;自己写原创文章太累了。当然&#xff0c;我并不是说你不能写。自己写原创文章是不现实的。时间是最大的问题。 也许有的站长朋友会问&…

自定义封装Mybatis的过程

手写持久层框架思路分析&#xff1a; 步骤1:加载数据库配置信息&#xff0c;包括数据库url&#xff0c;端口&#xff0c;数据库名字&#xff1b;加载所有sqlmapper文件。 步骤2: 创建javaBean&#xff0c;全局配置类&#xff0c;Configuration&#xff0c;映射配置类&#xff…

香橙派和树莓派基于官方外设开发

香橙派和树莓派基于官方外设开发 1.wiringPi外设SDK安装 方式一&#xff1a; git clone https://github.com/orangepi-xunlong/wiringOP //下载源码 cd wiringOP //进入文件夹 sudo ./build clean //清除编译信息 sudo ./build //编译方式二 通过windows浏览器打开https://…

全网最新网络安全学习路线

在各大平台搜的网安学习路线都太粗略了。。。。看不下去了&#xff01; 我把自己整理的系统学习路线&#xff0c;拿出来跟大家分享了&#xff01;点击查看详细路线图 建议的学习顺序&#xff1a; 一、网络安全学习普法&#xff08;心里有个数&#xff0c;要进去坐几年&#xf…

Java入门教程||Java 网络编程||Java 发送邮件

Java 网络编程 网络编程是指编写运行在多个设备&#xff08;计算机&#xff09;的程序&#xff0c;这些设备都通过网络连接起来。 java.net包中J2SE的API包含有类和接口&#xff0c;它们提供低层次的通信细节。你可以直接使用这些类和接口&#xff0c;来专注于解决问题&#…

基于simulink视频处理系统多核仿真(附源码)

一、前言 此示例演示如何使用 Simulink中的数据流执行域在多个内核上运行视频处理系统。 数据流执行域允许您在计算密集型系统的设计中使用多个内核。此示例演示数据流作为子系统的执行域如何提高模型的模拟性能。 二、视频中的对象计数 此示例演示如何使用基本形态运算符从…

Docker部署Doris超详细图文教程

Doris安装有非常多的方法&#xff0c;这里主要介绍Docker中使用dev容器用来学习和测试的方法&#xff0c;避免在其他教程中踩坑(生产环境不建议使用Docker安装) 这里介绍一个不踩坑的docker本地单机版&#xff0c;笔者安装环境为Windows下的Docker&#xff0c;若为Linux系统安装…

SwiftUI + Swift 设备振动

如何让设备振动呢 iPhone 6S 3D Touch&#xff0c;可以识别轻&#xff0c;中&#xff0c;重三种按压力度&#xff0c;配合恰到好处的振动有利于提升交互体验&#xff0c;但后面的新设备都不支持 3D Touch 了&#xff0c;改为了检测按压时间&#xff0c;按同一个图标&#xff0…

如何用canvans实现地图上的运动轨迹

1.先需要一个地图的图片 2.通过canvas绘制出运动轨迹 // 创建渐变function createGradient(context, p0, p1) {const gradient context.createLinearGradient(p0.x, p0.y, p1.x, p1.y);gradient.addColorStop(0, "rgba(255, 0, 255, 0)");gradient.addColorStop(1,…

颜色渐变的数据密集适用的堆叠图

一般情况会用柱状图去堆叠&#xff0c;但是如果数据量太大了&#xff0c;就可考虑这种方式堆叠。可以呈现时间和数量上不同层次数据的变化。 效果图&#xff1a; 比较详细的注释一下源码&#xff1a; import matplotlib as mpl import matplotlib.pyplot as plt import numpy …

第七章:L2JMobius学习 – 登录服务LoginServer讲解

在上一个章节中&#xff0c;我们学习了网络数据传输的封装network。那么&#xff0c;在本章的登录服务LoginServer的讲解中&#xff0c;我们就来使用一下这个封装好的功能。Network的封装需要我们继承很多的接口或类。我们首先查看一下登录服务LoginServer的文件结构&#xff0…

[Android 13]Binder系列--获取ServiceManager

获取ServiceManager hongxi.zhu 2023-7-1 以SurfaceFlinger为例&#xff0c;分析客户端进程如何获取ServiceManager代理服务对象 主要流程 SurfaceFlinger中获取SM服务 frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp // publish surface flingersp<…

mysql单表查询,排序,分组查询,运算符

CREATE TABLE emp (empno int(4) NOT NULL, --员工编号ename varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,--员工名字job varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,--员工工作mgr int(4) NULL DEFAULT NU…

随机产生50个100以内的不重复的整数,设计位图排序算法进行排序。

1.问题 随机产生50个100以内的不重复的整数&#xff0c;设计位图排序算法进行排序。 2.设计思路 阶段1&#xff1a; 初始化一个空集合    for i[0,n)    bit[i]0 阶段2&#xff1a; 读入数据i&#xff0c;并设置bit[i]1    for each i in the input file    bit[i]1…

Tomcat 应用服务 WEB服务

简述&#xff1a; 中间件产品介绍 目前来说IBM的WebSphere&#xff0c;Oracle的Weblogic占据了市场上Java语言Web站点的部分份额&#xff0c;该两种软件由于无与伦比的性能及可靠性等优势被广泛应用于大型互联网公司的Web场景中&#xff0c;但是其高昂的价格也使得中小型互联…

DL-FWI:数据(第二次培训作业)

代码&#xff1a; import scipy.io import matplotlib import numpy as np import matplotlib.pylab as plt matplotlib.use(TkAgg) from mpl_toolkits.axes_grid1 import make_axes_locatable import cv2font21 {family: Times New Roman,weight: normal,size: 21,}font18 …

【通览一百个大模型】LaMDA(Google)

【通览一百个大模型】LaMDA&#xff08;Google&#xff09; 作者&#xff1a;王嘉宁&#xff0c;本文章内容为原创&#xff0c;仓库链接&#xff1a;https://github.com/wjn1996/LLMs-NLP-Algo 订阅专栏【大模型&NLP&算法】可获得博主多年积累的全部NLP、大模型和算法干…

图像金字塔、滑动条、鼠标事件响应

1、拉普拉斯图像金字塔 1.1 原理 1.2 实现 //拉普拉斯图像金字塔 void test1() {//高斯图像金字塔构建Mat img imread("F:/testMap/lena.png");vector<Mat> Guass;int level 3;Guass.push_back(img);for (int i 0; i < level; i){Mat guass;pyrDown(Gua…

蓝桥杯每日一练专栏导读2

之前一直更新的是C、C相关的题目&#xff0c;但作为一名前端工程师&#xff0c;还是对Js了解的更多一些&#xff0c;所以从此以后停止更新C/C相关内容&#xff0c;改为更新Js相关的练习题。 内容 更新的内容依旧是蓝桥杯大赛官网提供的习题。每一道题都会提供详细的解题思路&a…