OpenThreads库介绍——Mutex

1. 简介

Mutex 是互斥体接口类。如同pthread 等常用的线程库那样,OpenThreads 也提供了互斥体操作的机制,它有效地避免了各个线程对同一资源的相互竞争,即,某一线程欲操作某一共享资源时,首先使用互斥体成员的lock()函数加锁,操作完成之后再使用unlock 函数解锁。一个线程类中可以存在多个Mutex 成员,用于在不同的地点或情形下为共享区域加锁;但是一定要在适当的时候解锁,以免造成线程的共享数据无法再访问。

2. 使用场景

互斥体主要是为了保护线程需要公共访问的资源,一般习惯上将公共资源和一个互斥体绑定在一起(比如封装成一个类)——也就是说为了访问这个公共资源必须首先lock互斥体,遵循这个约定后,如果线程A要访问这个公共资源,那么它先lock互斥体,开始对资源进行一些操作。此时如果线程B也要访问这个资源,此时它也需要lock互斥体,但是此刻互斥体被A线程lock了,导致B线程会被阻塞。当A访问完成之后,unlock互斥体。B线程发现互斥体可以lock了,于是继续运行,lock互斥体,获取资源。

3. 函数接口

OpenThreads::Mutex提供了3个简单的接口,正如上文提到的,包括lock和unlock,另外还提供了一个trylock,这个函数会检查是否可以获得互斥体,如果不可以返回false,否则lock到互斥体并返回true。

    virtual int lock();
    virtual int unlock();
    virtual int trylock();

trylock提供了一种可以测试的手段,不用想lock那样发现mutex被占用后导致现场阻塞,trylock是直接返回的,在某些情况下可能非常有用。

4. 示例


std::map<std::string, std::string> g_pages;
OpenThreads::Mutex g_pages_mutex;

class ThreadA : public OpenThreads::Thread
{
public:
    virtual void run()
    {
        // simulate a long page fetch
        Thread::microSleep(2000000);
        std::string result = "A-Content";

        g_pages_mutex.lock();
        g_pages["A"] = result;
        g_pages_mutex.unlock();
    }
};

class ThreadB : public OpenThreads::Thread
{
public:
    virtual void run()
    {
        // simulate a long page fetch
        Thread::microSleep(2000000);
        std::string result = "B-Content";

        g_pages_mutex.lock();
        g_pages["B"] = result;
        g_pages_mutex.unlock();
    }

};


int main()
{
    ThreadA a;
    ThreadB b;

    a.start();
    b.start();

    a.join();
    b.join();

    for (const auto &pair : g_pages) {
        std::cout << pair.first << " => " << pair.second << '\n';
    }

在需要访问公共的map资源时,需要首先lock互斥体,访问结束后需要unlock互斥体让出资源的所有权,让其他线程可以获得公共的资源。

5. 异常安全

在使用Mutex的时候,Mutex的lock和unlock一定要成对的出现,但是如果程序在lock之后,操作公共资源是出现异常,那么unlock用于也不会被调用,导致该线程崩溃,同时由于Mutex没有被释放,导致其他线程永远拿不到这个mutex,从而其他线程一直挂起。可以参考《OpenThreads库的使用-Mutex》 中的示例程序。

  • 解决方式
    解决的方法和C++中编写异常安全的代码处理机制完全一样,方式1是使用try…catch块,并在catch分支中添加Mutex的lock函数;另一种方式就是使用C++中的RAII,用一个类封装这个Mutex,由于C++保证栈上声明的类在推出作用域时,不管是否发生了异常,析构函数肯定会被调用,基于这个特征OpenThreds提供了Scoped这个模板函数,我们可以声明
    ScopedLock<Mutex>临时的变量来保证Mutex在发生异常之后仍然可以正确的释放Mutex,ScopedLock代码如下:

template <class M> class ScopedLock
{
    private:
        M& m_lock;
        ScopedLock(const ScopedLock&); // prevent copy
        ScopedLock& operator=(const ScopedLock&); // prevent assign
    public:
        explicit ScopedLock(M& m):m_lock(m) {m_lock.lock();}
        ~ScopedLock(){m_lock.unlock();}
};

如果使用这个函数,那么上面的run函数可以修改成:

    virtual void run()
    {
        // simulate a long page fetch
        Thread::microSleep(2000000);
        std::string result = "A-Content";

        OpenThreads::ScopedLock<OpenThreads::Mutex> mutloc(g_pages_mutex);
        g_pages["A"] = result;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值