锁-同步-多线程-单例-《程序员的自我修养》读书笔记

第一章

同步和加锁

数据访问同步:指的是一个线程访问数据未结束时,其他线程不得对同一个数据进行访问

同步的方法

1.二元信号量(和互斥量类似)
在这里插入图片描述
2.多元信号量(简称信号量
在这里插入图片描述
2.互斥量(和二元信号量类似)
在这里插入图片描述
3.临界区
在这里插入图片描述
4.读写锁
在这里插入图片描述
5.条件变量
在这里插入图片描述

信号量与条件变量:信号量一次唤醒一个线程,信号量可以一次让多个线程等待也可以一次唤醒多个线程
信号量与互斥量:互斥量本线程内使用,信号量可以跨线程
临界区与互斥量(信号量):互斥量和信号量跨进程可见的(互斥量既然可以跨进程不能跨线程吗?),临界区仅在本进程可见

过度优化

即使合理加锁也不一定能保证线程安全

情况一
在这里插入图片描述
在这里插入图片描述

这种情况主要原因在于编译器过度优化导致变量X的值没有被即时写回。

情况二
在这里插入图片描述
在这里插入图片描述

第二种情况的主要原因在于过度优化导致的执行顺序调整。前两种情况可以用volatile关键字进行限制,但也仅仅只是能限制编译器过度过度优化,不能限制cpu的动态调整

情况三
在这里插入图片描述
第一次判断null:检测对象是否已经存在,如果不存在则进入
第二次判断null:防止多线程时过多的锁等待
这两个锁加锁的详细逻辑是这样的:
首先原本的单线程单例是这样的:

Singleton* getInstance()
{
    if (instance == NULL) {
       instance = new Singleton();
    }
    return instance;
}

多线程时为了保证线程安全,加一把锁

Singleton* getInstance()
{
    lock();
    if (instance == NULL)
    {
       instance = new Singleton();
    }
    unlock();
    return instance;
}

但是这样多线程同时访问时,去判断空之前都会去加锁,大量并发时会有很多不必要的锁等待导致线程阻塞,
所以再在最外层进行一次判空,如果对象已经存在了就不必去锁定判空了。

Singleton* getInstance()
{
    if (instance == NULL)
    {
	lock();
    	if (instance == NULL)
    	{
       		instance = new Singleton();
    	}
    	unlock();
    }

    return instance;
}

在这里插入图片描述

针对上述情况可以使用barrier指令来限制CPU的动态调整,一条barrier指令会阻止CPU将该指令前的指令交换到barrier之后,反之亦然(阻止将之后的交换到之前) C++11修复这个问题(https://www.cnblogs.com/zxh1210603696/p/4157294.html)同时c++11提供一种简单的方法来实现单例 常用设计模式

在这里插入图片描述

可重入与线程安全

可重入:重新读入
函数被重入,只有两种情况:
1.多线程同时执行这个函数
2.函数自身调用自身
可重入必须具备如下特点:
在这里插入图片描述
可重入的函数在多线程下可安全使用,是并发的强力保障

多线程的内部情况

多线程在操作系统中(windows /linux)中是分用户态和内核态的,用户态和内核态之间的模型有三种:
1、一对一模型
2、多对一模型
3、多对多模型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值