如何编写一个线程安全的程序或者函数库?

来源:互联网 发布: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中的则未作处理。 )
原创粉丝点击