大家好,我是LinuxZn。
本次分享线程安全的基础知识。
线程安全
在多线程编程中,线程安全是必须要考虑的因素。
什么是线程安全?
在多线程环境中,多个线程在同一时刻对同一份资源进行写操作时,不会出现数据不一致。反之,则是线程非安全的。
线程安全是程序设计中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的公用变量,使程序功能正确完成。
为了确保在多线程环境中的线程安全,就要确保数据的一致性。确保线程安全的几种方法:
使用互斥锁
一个线程,如果需要访问公共资源,需要获得互斥锁并对其加锁,资源在在锁定过程中,如果其它线程对其进行访问,也需要获得互斥锁,如果获取不到,线程只能进行阻塞,直到获得该锁的线程解锁。关于互斥锁的使用:Hello系列 | 多线程编程基础!
例子(来源:维基百科):
#includeintincrement_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中的原子变量提供了一个可使此函数既线程安全又可重入(而且还更简洁)的替代方案:
#includeintincrement_counter(void) { staticstd::atomic counter(0); //incrementisguaranteedtobedoneatomically intresult=++counter; returnresult; }
Linux内核中原子整形操作:
#includeintincrement_counter(void) { atomic_tcounter=ATOMIC_INIT(0); //incrementisguaranteedtobedoneatomically atomic_inc(&counter); intresult=counter; returnresult; }
什么是原子操作?
从字面上简单理解,原子是一种很微小的粒子;原子操作是不能再进一步细分的操作。
从上面互斥锁的例子来看,在线程层面,线程1和线程2同时调用了increment_counter函数,被 mutex 保护的操作是原子操作,lock、unlock及保护部分要整体顺序运行,不可再进一步细分,作为一个原子存在 。
如果确定某个操作是原子的,并且有原子操作API函数可以使用,就不用为了去保护这个操作而加上会耗费昂贵性能开销的锁。
如,Linux内核原子整形操作 API 函数表(来源:正点原子) :
防止过度优化
线程安全的函数应该为每个调用它的线程分配专门的空间,把多个线程共享的变量正确对待(如,通知编译器该变量为“易失(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文章
3611浏览量
93680 -
中断
+关注
关注
5文章
898浏览量
41464 -
函数
+关注
关注
3文章
4326浏览量
62558 -
C++
+关注
关注
22文章
2108浏览量
73608 -
线程安全
+关注
关注
0文章
13浏览量
2457
原文标题:如何理解线程安全?
文章出处:【微信号:Linux大陆,微信公众号:Linux大陆】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论