0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何理解线程安全?

Linux大陆 来源:Linux大陆 2023-05-08 15:03 次阅读

大家好,我是LinuxZn。

本次分享线程安全的基础知识。

线程安全

在多线程编程中,线程安全是必须要考虑的因素。

什么是线程安全?

在多线程环境中,多个线程在同一时刻对同一份资源进行写操作时,不会出现数据不一致。反之,则是线程非安全的。

线程安全是程序设计中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的公用变量,使程序功能正确完成。

为了确保在多线程环境中的线程安全,就要确保数据的一致性。确保线程安全的几种方法:

使用互斥锁

一个线程,如果需要访问公共资源,需要获得互斥锁并对其加锁,资源在在锁定过程中,如果其它线程对其进行访问,也需要获得互斥锁,如果获取不到,线程只能进行阻塞,直到获得该锁的线程解锁。关于互斥锁的使用:Hello系列 | 多线程编程基础!

例子(来源:维基百科):

#include

intincrement_counter(void)
{
staticintcounter=0;
staticpthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_lock(&mutex);

//onlyallowonethreadtoincrementatatime
++counter;
//storevaluebeforeanyotherthreadsincrementitfurther
intresult=counter;

pthread_mutex_unlock(&mutex);

returnresult;
}

这个函数是线程安全的,可以在多个线程中被调用。

使用原子操作

上面的例子中,使用一个 互斥锁来保护一次简单的增量操作显然过于昂贵,我们可以使用一些专门的原子操作API函数来替代。如上述例子,c++11中的原子变量提供了一个可使此函数既线程安全又可重入(而且还更简洁)的替代方案:

#include

intincrement_counter(void)
{
staticstd::atomiccounter(0);

//incrementisguaranteedtobedoneatomically
intresult=++counter;

returnresult;
}

Linux内核中原子整形操作:

#include

intincrement_counter(void)
{
atomic_tcounter=ATOMIC_INIT(0);

//incrementisguaranteedtobedoneatomically
atomic_inc(&counter);
intresult=counter;

returnresult;
}

什么是原子操作?

从字面上简单理解,原子是一种很微小的粒子;原子操作是不能再进一步细分的操作。

从上面互斥锁的例子来看,在线程层面,线程1和线程2同时调用了increment_counter函数,被 mutex 保护的操作是原子操作,lock、unlock及保护部分要整体顺序运行,不可再进一步细分,作为一个原子存在 。

如果确定某个操作是原子的,并且有原子操作API函数可以使用,就不用为了去保护这个操作而加上会耗费昂贵性能开销的锁。

如,Linux内核原子整形操作 API 函数表(来源:正点原子) :

95134a9a-ecf0-11ed-90ce-dac502259ad0.png

防止过度优化

线程安全的函数应该为每个调用它的线程分配专门的空间,把多个线程共享的变量正确对待(如,通知编译器该变量为“易失(volatile)”型,阻止其进行一些不恰当的优化)。

线程安全函数与可重入函数?

先明确概念:

线程安全函数:能够正确地处理多个线程之间的公用变量的函数。、

可重入函数:在任意时刻被中断然后操作系统调度执行另一段代码,这段代码又使用了该副程序不会出错。

可重入函数应当满足条件:

不能含有静态(全局)非常量数据。

不能返回静态(全局)非常量数据的地址。

只能处理由调用者提供的数据。

不能依赖于单例模式资源的锁。

调用(call)的函数也必需是可重入的。

可重入函数未必是线程安全的;线程安全函数未必是可重入的。

例子1:上述例子中的increment_counter函数是线程安全的,但是并不是可重入的。因为使用了互斥锁,如果这个函数用在可重入的中断处理程序中,如果在pthread_mutex_lock(&mutex)和pthread_mutex_unlock(&mutex)之间产生另一个调用函数increment_counter的中断,则会第二次执行此函数,此时由于mutex已被lock,函数会在pthread_mutex_lock(&mutex)处阻塞,并且由于mutex没有机会被unlock,阻塞会永远持续下去。

例子2:一个函数打开某个文件并读入数据。这个函数是可重入的,因为它的多个实例同时执行不会造成冲突;但它不是线程安全的,因为在它读入文件时可能有别的线程正在修改该文件,为了线程安全必须对文件加“同步锁”。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3441

    浏览量

    92410
  • 中断
    +关注

    关注

    5

    文章

    884

    浏览量

    41028
  • 函数
    +关注

    关注

    3

    文章

    3896

    浏览量

    61310
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72900
  • 线程安全
    +关注

    关注

    0

    文章

    13

    浏览量

    2440

原文标题:如何理解线程安全?

文章出处:【微信号:Linux大陆,微信公众号:Linux大陆】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    不同创建线程安全Set的方式

    线程安全的问题,真的算是老生常谈了。这几天看到一个 HashSet 线程安全的骚操作,在这里分享给大家。 在本文中,我们将分享如何构造线程
    的头像 发表于 09-25 14:20 353次阅读

    调用非安全线程的dll的问题

    在调用非线程安全的dll时,是不是要选择在UI线程中运行?是不是还必须用不可重入的子VI封装一下?上述的两步是不是都要做?这些问题不是很清楚,还请各位大神指点一下
    发表于 03-14 21:13

    YYKit源码线程安全计数器YYSentinel

    YYKit源码探究(八十三) —— 线程安全计数器YYSentinel(一)
    发表于 04-28 16:57

    Linux下的线程安全是什么

    Linux下的线程安全原文结构有点乱线程安全:多个执行流对临界资源进行争抢访问,而不会造成数据二义性和逻辑混乱,成这段代码的过程是线程
    发表于 07-01 13:34

    什么是线程安全?如何去实现线程安全

    什么是线程安全?如何去实现线程安全?互斥实现的技术是什么?有哪些注意事项?同步实现的技术是什么?其操作流程有哪些?
    发表于 07-23 09:57

    请教大神rtthread中的ringbuff是线程安全的吗

    最近想用轻量级的ringbuff,请教大神rtthread中的ringbuff是线程安全的吗?
    发表于 07-29 10:44

    什么是线程安全

    线程安全的链表-队列-栈,就是多线程同时操作(包括查找、添加、删除等)链表、队列或栈,无论如何操作,就是多线程同时操作(包括查找、添加、删除等)链表、队列或栈,无论如何操作,都不会产生
    发表于 11-17 11:16 1次下载

    java的线程安全、单例模式、JVM内存结构

    线程安全就是多线程访问时,采用了加锁机制,当一个线程访问类的某个数据时,进行保护,其他线程不能进行访问直到该
    发表于 03-12 10:30 0次下载

    什么是线程安全 如何实现线程安全代码

    相信有很多同学在面对多线程代码时都会望而生畏,认为多线程代码就像一头难以驯服的怪兽,你制服不了这头怪兽它就会反过来吞噬你。
    的头像 发表于 05-17 12:45 1432次阅读

    理解析:线程池中多余的线程是如何回收的?

    最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了。
    的头像 发表于 11-11 09:57 693次阅读

    什么是线程安全?如何理解线程安全

    在多线程编程中,线程安全是必须要考虑的因素。
    的头像 发表于 05-30 14:33 1510次阅读
    什么是<b class='flag-5'>线程</b><b class='flag-5'>安全</b>?如何<b class='flag-5'>理解</b><b class='flag-5'>线程</b><b class='flag-5'>安全</b>?

    线程安全怎么办

    线程安全一直是多线程开发中需要注意的地方,可以说,并发安全保证了所有的数据都安全。 1 线程
    的头像 发表于 10-10 15:00 197次阅读
    <b class='flag-5'>线程</b><b class='flag-5'>安全</b>怎么办

    如何知道你的代码是否线程安全

    在并发编程时,如果多个线程访问同一资源,我们需要保证访问的时候不会产生冲突,数据修改不会发生错误,这就是我们常说的 线程安全 。 那什么情况下,访问数据时是安全的?什么情况下,访问数据
    的头像 发表于 11-01 11:42 320次阅读
    如何知道你的代码是否<b class='flag-5'>线程</b><b class='flag-5'>安全</b>

    如何实现一个多读多写的线程安全的无锁队列

    加锁。那么如何实现一个多读多写的线程安全的无锁队列呢? 互斥锁:mutexqueue(太简单不介绍了) 互斥锁+条件变量:blockqueue(太简单不介绍了) 内存屏障:lockfreequeue
    的头像 发表于 11-08 15:25 456次阅读
    如何实现一个多读多写的<b class='flag-5'>线程</b><b class='flag-5'>安全</b>的无锁队列

    redis多线程还能保证线程安全

    Redis是一种使用C语言编写的高性能键值存储系统,它是单线程的,因为使用了多路复用的方式来处理并发请求。这样的实现方式带来了很好的性能,但同时也引发了一些线程安全方面的问题。 在Redis中,由于
    的头像 发表于 12-05 10:28 673次阅读