C++并发编程2——为共享数据加锁(四)

C++并发编程 线程间的数据共享 1. 为什么要并发?分离关注点、提升性能 2. 何时不用并发?收益比不上成本。 启动线程存在固有开销 线程需要独立的堆栈空间 运行线程时,操作系统每次上下文切换都需要耗费时间 3. C++11支持的并发:全新的线程感知内存模型,以及管理线程、保护共享数据、线程同步和原子操作等 4. 线程:启动线程 、等待线程结束join() 或自主运行detached() (当线程退出时, 相关资源的能够正确回收) 5. 线程间共享数据 条件竞争(race condition):不变量遭到破坏(比如双向... 阅读详情

如果某个想法是你唯一的想法,再也没有比这个更危险的事情了。

本节会阐述保护共享数据的替代方案,很多情况下,使用互斥量并不合适,会带来性能消耗。下文会详细讲解集中通用的场景。

保护共享数据的初始化过程

为了防止共享数据初始化时数据被破坏,C++提供了std::once_flag和std::call_once来保证共享数据初始化的正确性。

// using mutex
std::shared_ptr<some_resource> resource_ptr;
std::mutex resource_mutex;
void foo() 
{
    std::unique_lock<std::mutex> lk(resource_mutex);
    if(!resource_ptr)
    {
        resource_ptr.reset(new some_resource);
    }
    lk.unlock();
    resource_ptr->do_something();
}

// using call_once
std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;

void int_resource()
{
    resource_ptr.reset(new some_resource);
}

void foo()
{
    std::call_once(resource_flag, init_resource);
    resource_ptr->do_something();
}

保护很少更新的数据量

对于这种共享数据可以采用“读者-写着锁”,其允许两种不同的使用方式:一个作者线程独占访问和共享访问,让多个读者线程并发访问。

C++标准并没有提供相关的解决方案,我们可以使用boost::shared_mutex来做同步。对于更新操作,可以使
用std::lock_guard<boost::shared_mutex>和std::unique_lock<boost::shared_mutex>进行上锁;对于访问操作,可以使用boost::shared_lock<boost::shared_mutex>获取共享访问权。我们来看下面例子:

#include <map>
#include <string>
#include <mutex>
#include <boost/thread/shared_mutex.hpp>
class dns_entry;
class dns_cache
{
  std::map<std::string,dns_entry> entries;
  mutable boost::shared_mutex entry_mutex;
public:
  dns_entry find_entry(std::string const& domain) const
  {
    boost::shared_lock<boost::shared_mutex> lk(entry_mutex); 
    std::map<std::string,dns_entry>::const_iterator const it=
       entries.find(domain);
    return (it==entries.end())?dns_entry():it->second;
  }
  void update_or_add_entry(std::string const& domain,
                           dns_entry const& dns_details)
  {
    std::lock_guard<boost::shared_mutex> lk(entry_mutex);
    entries[domain]=dns_details;
} };

上面代码,find_entry()使用了boost::shared_lock<>实例来保护器共享和只读权限;update_or_add_entry()使用std::lock_guard<>实例来独占访问权限。

嵌套锁

对于一个已经上锁的互斥量多次上锁,会出现未定义行为。然而对于嵌套锁std::recursive_mutex来说,多次上锁不会出现问题。

在互斥量锁住其他线程前,你必须释放你拥有的所有 锁,所以当你调用lock()三次时,你也必须调用unlock()三次。正确使
用 std::lock_guard<std::recursive_mutex>和 std::unique_lock<std::recursice_mutex>可以帮我们处理这些问题。

大多数情况下,嵌套锁是用在可被多线程并发访问的类上,所以其拥有一个互斥量保护其成员数据。每个公共成员函数 都会对互斥量上锁,然后完成对应的功能,之后再解锁互斥量。不过,有时一个公共成员函 数会调用另一个公共函数作为其操作的一部分。

不过上面提高的方案是不推荐的,推荐的做法是——从中提取出一个函数作为类的私有成员, 并且让所有成员函数都对其进行调用,这个私有成员函数不会对互斥量进行上锁(在调用前必 须获得锁)。


关注微信公众号
C++并发编程--第三章--线程间共享数据 1. 线程间共享数据存在的问题 所有线程间共享数据的问题,都是修改数据导致的(竞争条件)。如果所有的共享数据都是只读的,就没问题,因为一个线程所读取的数据不受另一个线程是否正在读取相同的数据而影响。(引用https://blog.youkuaiyun.com/xy_cpp/article/details/80646443)。 2. 利用锁(mutex)保护共享数据 2.1 使用C++11中的std::mutex 在C++11中,你可以通过std::mutex的构造函数创建一个std::mutex实例,通过调用l 阅读详情

相关推荐

windowsC++多线程加锁信号量共享内存

windowsC++多线程加锁信号量共享内存

将读写锁放到共享内存中,实现进程之间对数据的读写访问控制

代码 #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <assert.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <fstream> #include <

CHYabc123456hh的博客 685

ThingsKit物联网平台产品管理之物模型

物模型是ThingsKit物联网平台为产品定义的数据模型,用于描述产品的功能。本文介绍物模型相关概念和使用限制。

云腾五洲官方博客 1174

C++并发编程2——为共享数据加锁(三)

死锁的概念略去不说,死锁有可能发生在使用多个互斥量的场景下,也可能存在没有使用互斥量的场景

小熊糖否——木子兮的自留地 3154

【生成数据集EXCEL文件】使用生成对抗网络GAN生成数据集:输出生成数据集EXCEL

生成器的任务是接收随机噪声向量作为输入,并尽可能生成与真实数据相似的样本,而判别器则是一个二分类器,旨在区分输入的样本是来自生成器生成的,还是来自真实数据。在训练过程中,生成器和判别器通过对抗学习的方式相互博弈,生成器的目标是生成足以欺骗判别器的样本,而判别器的目标则是尽可能准确地判断样本的真实性。GAN网络在多个领域展现了强大的能力,能够生成与训练数据分布相似的新数据,包括但不限于图像生成、图像风格转换、超分辨率、数据增强、视频生成、自然语言处理、医学图像处理以及游戏与虚拟现实等。

hustef的博客 1521

冒泡排序的并行计算

本人也是并行计算的小白,在这里记录下自己的学习笔记。 今天做了一下冒泡排序的并行计算比较: 串行冒泡排序如下: #include <stdio.h> #include <stdlib.h> #include <windows.h> void BubbleSort(int * pData, int nSize) { if (!pData) { ...

gaoyi的专栏 4491

Linux进程间通信之互斥锁(共享内存实现),包括C++11的事件,信号量

#pragma once #include <mutex> #include <condition_variable> #include <chrono> #include <string> #include <memory> #ifndef _WIN32 #include <unistd.h> #include <semaphore.h> #include <stdio.h> #include <fcn

阿乐的博客 4050

EB tresos配置指南:CAN通信信号收发全流程解析

本文详细解析了使用EB tresos配置工具实现AUTOSAR架构下CAN通信信号收发功能的全流程。从环境准备、COM模块信号定义、PduR路由配置、CanIf硬件抽象到CAN驱动层设置,提供了清晰的步骤指南和关键配置要点,帮助开发者快速打通从应用层到物理层的数据链路,实现可靠的CAN通信。

weixin_29192141的博客 508

c++实现读写共享锁

一、使用互斥锁和条件变量实现读写锁: [cpp] view plaincopy class readwrite_lock   {   public:       readwrite_lock()           : stat(0)       {       }          void readLock()

lishaoyu的专栏 1023

瑞芯微RK3588系统重装实战:从Windows到Linux的完整烧录指南

本文提供了一份详尽的瑞芯微RK3588开发板系统重装实战指南,涵盖从Windows到Linux双平台的完整烧录流程。针对RK3588芯片特性,详细解析了驱动安装、工具使用、固件烧录步骤及常见问题排错,旨在帮助开发者高效、安全地完成系统切换与部署,快速投入AI推理与边缘计算等应用开发。

weixin_29164185的博客 360

THUCNews新闻文本分类-tfidf+sklearn

本文使用的是和鲸社区现有数据集,代码也在和鲸社区公开,fork之后可以直接运行。 本文中所用的数据集清华NLP组提供的THUCNews新闻文本分类数据集的一个子集(原始的数据集大约74万篇文档,训练起来需要花较长的时间)。 本次训练使用了其中的体育, 财经, 房产, 家居, 教育, 科技, 时尚, 时政, 游戏, 娱乐10个分类,每个分类6500条,总共65000条新闻数据。项目在和鲸社区的平台上跑的,数据集直接引用了和鲸的数据集,每个分类6500条,总共65000条新闻数据。 数据集划分如下: cnews

wmq104的博客 4591

C++并发编程 -2.线程间共享数据

本章就以在C++中进行安全的数据共享为主题。避免上述及其他潜在问题的发生的同时,将共享数据的优势发挥到最大。

qq_45604814的博客 2003

Android 13.0 app进程保活白名单功能实现

在13.0的系统rom产品开发中,在某些重要的app即使进入后台,产品需求要求也不想被系统杀掉进程,需要app长时间保活,就是app进程保活白名单功能的实现, 所以需要在系统杀进程的时候不杀掉白名单的进程,接下来就看怎么样来实现这些功能

安卓兼职framework和app工程师的博客 3142

C++并发编程实战 第三章 在线程间共享数据

private:public:// 1 在构造函数体中的执行拷贝// 在调用pop前,检查栈是否为空// 在修改堆栈前,分配出返回值data.pop();return res;data.pop();

qq_52758467的博客 530

YOLO11 锚框机制全解析:从改进 K-means 聚类到自适应优化,附多场景实战(附代码)

摘要 YOLO11的锚框机制采用"自适应、动态化、场景感知"的设计理念,通过数据驱动的锚框生成、多尺度感知分配和实时优化策略,显著提升目标检测性能。相较于早期YOLO版本,YOLO11的锚框完全基于训练集目标分布自适应生成,并引入智能分配策略,使不同尺寸目标获得最优匹配。技术架构包含自适应锚框生成、多尺度分配器和动态优化模块,通过改进的K-means聚类和交并比计算实现精准匹配。YOLO11锚框机制在检测精度、训练稳定性和场景适应性方面均有显著提升,为复杂场景下的目标检测提供更优解决方案

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 1319

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

C++并发编程实战 第三章 线程间共享数据

add_to_list() 和 list_contains() 函数使用 std::lock_guard 来确保对 some_list 的访问是互斥的,即一个线程在修改列表时,其他线程不能读取或修改该列表。除了上述特性外,std::unique_lock 还支持锁所有权的转移,这意味着你可以将一个 std::unique_lock 持有的锁转移给另一个 std::unique_lock 实例。因为两个线程可能尝试在相同的一对实例之间交换数据,但选择了相反的锁定顺序,从而导致死锁。

_ 838

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

《C++并发编程实战》精读总结:第三章 在线程间共享数据

多线程数据共享的问题多是由于对数据的改动引起的,要对共享的数据进行保护,比较直接的方法就是实用互斥和加锁。下面看一个简单的例子,使用到了互斥mutex,和对互斥加锁lock_guard<>。这里,假设一个线程调用add向stack1里添加元素时,如果另外一个线程调用top_value想要看stack顶部的元素是无法做到的,因为add已经锁住了互斥mutex1。注意,互斥标记的是代码。锁住互斥就是锁住标记代码涉及到的共享数据。

见牛羊的博客 299

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

C++并发编程2——为保护数据加锁(一)

在应届生面试的时候,很多面试官都会问——“多线程如何共享资源”。在操作系统层面上可以给出若干关键词答案,但是在语言层面,这个问题考虑的就没有那么简单了。

小熊糖否——木子兮的自留地 6318

C++ 加锁的原则

规则1 多线程、进程并行访问共享资源时,一定要加锁保护 说明:共享资源包括全局变量,静态变量,共享内存,文件等。 建议封装像智能指针一样的对象对锁进行管理,比如我们就封装了一个auto_lock,在构造时申请 锁,析构中释放锁,保证不会忘记“解锁”。如果锁的作用范围有限,则可以这样: do { auto_lock lock(&lock); //… }while(0); 规则2 锁的职责单一 说明:每个锁只锁一个唯一共享资源;这样,才能保证锁应用的单一,也能更好的确保加锁的范围尽 量小。

GoodLinGL的博客 2908

16.C++11线程—在线程间共享数据(笔记)

在并发编程中,操作由两个或多个线程负责,它们争先让线程执行各自的操作,而结果取决于它们执行的相对次序,所有这种情况都是条件竞争。有时候,这直观、易懂,因为诸多互斥的用途各异,也会出现棘手的状况,例如,运用多个互斥分别保护多个独立的实例,这些实例属于同一个类。可是,如果选用了固定的次序(两个对象通过参数传入,我们总是先给第一个实例的互斥加锁,再轮到第二个实例的互斥),前面的建议就适得其反:针对两个相同的实例,若两个线程都通过该函数在它们之间互换数据,只是两次调用的参数顺序相反,会导致它们陷入死锁!

C++方向学习笔记 940

《C++并发编程实战》之第3章 线程共享

c++ 并发编程 数据共享

yxqq378287007的博客 1770

C++并发编程之二 在线程间共享数据

C++并发之探索编程二实战

Awesomewan的博客 1966

C++并发编程-3.互斥与死锁

本文介绍如何使用互斥量保证共享数据的安全,并讲述死锁的相关处理方案。

yang_pi_pi的博客 1111
上一篇: C++并发编程2——为共享数据加锁(三)
下一篇: 如何设计高效的C++类
JerkWisdom
博客等级 码龄14年 70粉丝 76原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值