如何编写一个线程安全的程序或者函数库?
来源:互联网 发布:faded软件下载 编辑:程序博客网 时间:2024/05/17 01:15
何为线程安全?
在多线程环境中编程,大家一直都强调线程安全,可以什么是线程安全呢?
依据《Java 并发编程实践》/《Java Concurrency in Practice》一书,一个线程安全的Class
应当满足三个条件:
- 从多个线程访问时,其表现出正确的行为
- 无论操作系统如何调度这些线程,无论这些线程的执行顺序如何交织
- 调用端代码无需额外的同步或其他协调动作
具体说来,线程安全性体现在:
线程安全的函数通过“锁”来保护共享资源不被并发地访问。“线程安全”仅关心函数的实现,而不影响其外部接口。
如何实现线程安全?
实现线程安全的主要方法就是确保多线程调用共享资源时能够正确,有序的调用。
可以对共享资源加互斥锁、原子操作等方法。
本文主要介绍如何用互斥锁确保共享资源调用的线程安全。
Mutex和MutexLock
首先介绍两个比较重要的工具类, 这两个工具类在C++11的标准库中都有实现。
Mutex
Mutex 是封装临界区,主要实现互斥器的创建和销毁。 临街区在Windows中是 CRITICAL_SECTION
, 而在Linux上是pthead_mutex_t
. 在C++11中的标准库中即是std::mutex
MutexLock/MutexGuard
MutexLock主要是负责临界区的进入和退出,也就是加锁和解锁。 在MutexLock执行构造函数时进行加锁操作,在执行析构函数时执行解锁操作,所以MutexLock的作用域就是自己的生命周期。 一般是函数内的局部变量。在C++11的实现是std::lock_guard
Mutex和MutexLock两个类都采用了RAII风格进行封装,这样封装的好处是保证了互斥锁出现死锁等现象。
具体的代码实现可以参考一下代码片。
#include <iostream>#include <pthread.h>using namespace std;class MutexLock{public: MutexLock() { pthread_mutex_init(&mutex_, NULL); } ~MutexLock() { pthread_mutex_destroy(&mutex_); } void Lock() { pthread_mutex_lock(&mutex_); } void Unlock() { pthread_mutex_unlock(&mutex_); }private: MutexLock(const MutexLock&); MutexLock& operator=(const MutexLock&); pthread_mutex_t mutex_;};class MutexLockGuard{public: explicit MutexLockGuard(MutexLock& mutex): mutex_(mutex) { mutex_.Lock(); } ~MutexLockGuard() { mutex_.Unlock(); }private: MutexLockGuard(const MutexLockGuard&); MutexLockGuard& operator=(const MutexLockGuard&); MutexLock& mutex_;};class Foo{public: void print() { MutexLockGuard lock(mutex_); cout << "Hello" << endl; }private: MutexLock mutex_; };
当然,如果利用C++11的特性,实现Class Foo
的线程安全,代码可以简化为:
#include <thread>#include <mutex>#include <iostream>class Foo{public: void print() { std::lock_guard<std::mutex> lock(p_mutex); cout << "Hello" << endl; }private: std::mutex p_mutex; };
通过以上例子, 在一个函数内部调用MutexLock可以实现一个临街区,确保共享资源正常调用。
从Stackoverflow借用的答案可以具体阐述如何确保我们的程序是线程安全的。
- 明确哪些资源是在多线程应用中被共享的
- 创建一个Mutex, 并在任何你需要访问共享资源之前加锁。(这些共享资源最好是private to class, 否则你不确定是否全面的保护共享资源)
- 清除掉全局变量。 最好不要用全局变量。
- 注意
static
关键字, 带有static
的函数是不可重入的, 如果想保证线程安全,需要加静态互斥锁。 (从这篇文章可以看出,不同的编译器似乎对static变量的处理不同,gcc中的static变量是线程安全的,VC中的则未作处理。 )
- 如何编写一个线程安全的程序或者函数库?
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 编写线程安全的JSP程序
- 如何编写线程安全的DLL
- 如何编写线程安全的DLL
- 如何编写线程安全的DLL
- 如何编写线程安全的DLL
- 如何编写一个安全的read函数
- 如何实现一个线程安全的map?
- CSS body 100%无效果
- log-error set to '/usr/local/mysql/log/mysqld.log', however file don't exists
- svn添加用户,权限
- SparkCore抽象之RDD
- SQL优化总结---百万数据
- 如何编写一个线程安全的程序或者函数库?
- String、StringBuffer与StringBuilder之间区别
- 网上转的类图文档
- JS动态添加option和删除option(附实例代码)
- JAVA学习准备
- Java for循环后大括号
- 教学用上云办公,老师省事了
- 吴恩达 机器学习笔记二(lecture 2)(损失函数、梯度下降)
- btrfs cfq, noop, deadline三种IO调度策略下的IO性能表现(gp针对grup.conf配置)