我们是多么渴望各种C++类都是多线程安全的,然而一旦涉及到对象间的交互,这样的渴望可能就只能是奢望了。下面,我们以设计一个双向链结点为例,看看要使其多线程安全将会带来一些什么问题。

class DoublyLinedNode{

DoublyLinedNode* pPrevNode_;

DoublyLinedNode* pNextNode_;

public:

DoublyLinedNode() : pPrevNode_(0), pNextNode_(0){}

virtual ~DoublyLinedNode();

public:

const DoublyLinedNode* GetPrevNode() const{return pPrevNode_;}

const DoublyLinedNode* GetNextNode() const{return pNextNode_;}

public:

void InsertPrevNode(DoublyLinedNode* p);

void InsertNextNode(DoublyLinedNode* p);

void Break();

};

这是一个简单的双向链结点类,我们就讨论讨论其Break接口,这个接口的作用是使结点从其所在的链中断开,如图:

它的实现可能是这样的:

void DoublyLinedNode::Break()

{

if (pPrevNode_)

{

pPrevNode_->pNextNode_ = pNextNode_;

}

if (pNextNode_)

{

pNextNode_->pPrevNode_ = pPrevNode_;

}

pPrevNode_ = 0;

pNextNode_ = 0;

}

这个实现是单线程模式的,没有多线程安全性。

第一次尝试:

void DoublyLinedNode::Break()

{

Lock();

if (pPrevNode_)

{

pPrevNode_->pNextNode_ = pNextNode_;

}

if (pNextNode_)

{

pNextNode_->pPrevNode_ = pPrevNode_;

}

pPrevNode_ = 0;

pNextNode_ = 0;

UnLock();

}

我们第一次尝试将这个接口的代码用多线程锁锁住了,然而问题很明显:

if (pPrevNode_)

{

pPrevNode_->pNextNode_ = pNextNode_;

}

if (pNextNode_)

{

pNextNode_->pPrevNode_ = pPrevNode_;

}

我们这两个对前向和后向结点的操作是修改另外两个对象的内部状态,多线程中,可能在此时正好有其他线程在对这两个对象进行操作(访问),或许程序就会因此而崩溃。

第二次尝试:

void DoublyLinedNode::Break()

{

Lock();

if (pPrevNode_)

{

pPrevNode_->SetNextNode(pNextNode_); // SetNextNode同样添加了锁保护

}

if (pNextNode_)

{

pNextNode_->SetPrevNode(pPrevNode_); // SetPrevNode同样添加了锁保护

}

pPrevNode_ = 0;

pNextNode_ = 0;

UnLock();

}

这第二次尝试将我们对前向和后继结点的内部状态的直接修改改成了对其接口的调用,我们试图通过在其各种接口中加锁来达到多线程安全的目的。然而这却引入了新的问题,我们在一个被锁住的代码中进行了又调用了另外会使用锁的代码,这最可能引发的问题就是资源竞争,而在我们这次尝试中引如的问题的确就是资源竞争,导致死锁:

我们在不同线程中对结点1和结点2同时调用Break,当1申请到自身的锁之后,准备调用2的接口,此时2也申请到了自身的锁,准备调用1的接口。由于1已经占有了自身的锁,2也占有了自身的锁,那么1将会在调用2的接口的地方等待2的锁,而2将会在调用1的接口的地方等待1, 1和2的相互等待就形成了死锁。

第三次尝试:

void DoublyLinedNode::Break()

{

Lock();

if (pPrevNode_)

{

pPrevNode_-> Lock();

pPrevNode_->SetNextNode(pNextNode_);

pPrevNode_-> UnLock ();

}

if (pNextNode_)

{

pNextNode_-> Lock();

pNextNode_->SetPrevNode(pPrevNode_);

pNextNode_-> UnLock ();

}

pPrevNode_ = 0;

pNextNode_ = 0;

UnLock();

}

这次尝试显得比较愚蠢,将外部对象加锁的过程提到了自身Break当中效果和第二次尝试是一样的,没有得到任何的改善。

第四次尝试:

void DoublyLinedNode::Break()

{

SharedLock();

if (pPrevNode_)

{

pPrevNode_->SetNextNode(pNextNode_);

}

if (pNextNode_)

{

pNextNode_->SetPrevNode(pPrevNode_);

}

pPrevNode_ = 0;

pNextNode_ = 0;

SharedUnLock();

}

这次尝试取得了一定的成功,对于这些关系密切,存在相互调用的对象,我们使用了共享锁,它的确将我们的多线程访问冲突和死锁问题解决了,但是这个共享锁的实现难度是相当大的,你必须要保证可能产生相互调用的对象都要进行锁共享,那么你对于增加、修改、删除对象这些管理工作将会变得极度困难,稍有差池就会引发问题,而且别人在使用你的类的时候也同样需要处处小心,这不是我们所期望的。

以上我们进行了四次尝试将我们的双向链结点类设计成多线程安全,显然我们已经筋疲力尽,却未能达到满意的效果。

在这里我建议大家设计这种类的时候尽量设计成单线程模式,在框架设计中去考虑多线程问题,比如使用单线程访问对象,而模块间使用异步通信来进行交互等。

多线程编程的确非常困难,C++在这方面又表现得力不从心,我在这里引入这个问题旨在于告诫大家在对待多线程问题上一定要细心细心再细心。

转载于:https://www.cnblogs.com/huty/p/8518634.html

【VS开发】C++线程安全相关推荐

  1. Linux下c开发 之 线程通信

    Linux下c开发 之 线程通信 1.Linux"线程" 进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种"多进程单线 ...

  2. 《Servlet学习笔记》Servlet开发细节-线程安全

    Servlet开发细节-线程安全 当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此servic ...

  3. iOS开发之线程间的MachPort通信与子线程中的Notification转发

    如题,今天的博客我们就来记录一下iOS开发中使用MachPort来实现线程间的通信,然后使用该知识点来转发子线程中所发出的Notification.简单的说,MachPort的工作方式其实是将NSMa ...

  4. Android开发之线程池管理ThreadPoolExecutor和Executors.newSingleThreadExecutor()

    在Android开发中网络请求数据在Android4.0以后禁止在主线程请求,那么我们只有新开启线程请求数据了 一般都喜欢简单点这样写: new Thread(new Runnable() {@Ove ...

  5. Linux下c开发 之 线程通信与pthread_cond_wait()的使用

    pthread_cond_wait() /************pthread_cond_wait()的使用方法**********/ pthread_mutex_lock(&qlock); ...

  6. Linux下c开发 之 线程通信(转)

    1.Linux"线程" 进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种"多进程单线程"的操作系统.Linu ...

  7. Android单例模式和线程安全,Android 开发单例模式线程安全与序列化

    前言 单例模式是最常用到的设计模式之一,熟悉设计模式的朋友对单例模式都不会陌生.一般介绍单例模式都只会提到饿汉式和懒汉式这两种实现方式. 看完本章后,你可能会发现项目中的并没有正确的使用创建单例,本文 ...

  8. HarmonyOS学习路之开发篇——线程管理

    什么是线程管理 不同应用在各自独立的进程中运行.当应用以任何形式启动时,系统为其创建进程,该进程将持续运行.当进程完成当前任务处于等待状态,且系统资源不足时,系统自动回收. 在启动应用时,系统会为该应 ...

  9. jmeter学习指南之详解US六仔源码开发jmeter线程组

    线程组元件US六仔源码开发 dsluntan.com Q:3393756370 VX:17061863513是任何一个测试计划的开始点.在一个测试计划中的所有元件都必须在某个线程下.所有的任务都是基于 ...

  10. javaweb学习总结二十三(servlet开发之线程安全问题)

    一:servlet线程安全问题发生的条件 如果多个客户端访问同一个servlet时,发生线程安全问题,那么它们访问的是相同的资源.如果访问 的不是相同资源,则不存在线程安全问题. 实例1:不会产生线程 ...

最新文章

  1. HTTP,FTP,TCP,UDP及SOCKET
  2. 深圳SEO外包公司×××,如何选择合适的SEO外包公司?
  3. Think in Java读书笔记--隐藏实施过程
  4. 加油站都需要什么手续_农村买房过户都需要什么手续?
  5. asp.net使用My97 Date Picker时设置默认起始时间为n年之前的今天
  6. Java 10 var关键字详解和示例教程
  7. 洛谷——P1680 奇怪的分组
  8. mysql 使用存储过程批量插数据
  9. 【转载】jQuery框架
  10. 卓越的社会化营销人的6个习惯
  11. tlwn726n无线网卡Linux驱动,tl-wn726n无线网卡驱动下载
  12. 防火墙和IPS有什么区别
  13. 雅虎邮箱pop服务器,使用Yahoo.com.cn的POP和SMTP
  14. 我花了一夜用数据结构给女朋友写个H5走迷宫游戏
  15. linux 命令 cups,linux cups 打印机命令说明
  16. python数据分析师前景及待遇怎么样_数据分析师未来五年发展前景怎么样?
  17. 渗透测试工具之——启明天镜BS版本概述
  18. 邵阳一中2021高考成绩查询,2021年邵阳高考状元名单公布,邵阳文理科状元是谁多少分...
  19. 高红冰:1个贫困县连接280个城市 电商减贫潜力巨大
  20. Spotlight on Windows监控软件

热门文章

  1. Java Object中的clone方法
  2. Scala方法定义及调用
  3. Linux ln指令
  4. Lua脚本式编程示例
  5. Linux安装gcc
  6. Hadoop大数据——mapreduce中的Combiner/序列化/排序初步
  7. Hibernate的配置详解
  8. 平滑均值滤波讲解-Matlab
  9. 记录navigator实现不同设备页面跳转
  10. 安卓canvas设置HTML,安卓开发中view和canvas的理解