为了异常安全(swap,share_ptr)——Effecive C++

互斥锁:

假设我们要在多线程中实现背景图片的控制:

class PrettyMenu{
public:
    ……
    void changeBackground(std::istream& imgSrc);//改变背景图片
    ……
private:
    Mutex mutex;    //互斥量
    Image* bgImage; //当前背景图片
    int imageChanges;   //背景图片改变次数
};

void PrettyMenu::changeBackground(std::istream& imgSrc)
{
    lock(&mutex);
    delete bgImage;
    ++imageChanges;
    bgImage=new Image(imgSrc);
    unlock(&mutex);
}

但是上面这些会遇到两个问题:

  • 如果 new Image(imgSrc)发生了异常,那么unlock就不会被执行,会被一直锁住
  • 发生上面异常,imageChanges也被累加,但实际上新的图片没有被安装

对于第一个问题:

被及时释放的互斥锁:

share_ptr我们可以指定它的删除器,所以可以在删除器中释放互斥锁.
如此即使是发生了异常,互斥锁也能够被及时的释放。

class Lock{
public:
    explicit Lock(Mutex* mu):mutexPtr(mu,unlock)//以某个Mutex初始化,unlock作为删除其
    {
        lock(mutexPtr);
    }
private:
    shared_prt<Mutex> mutexPtr;
};

如果通过一个Lock类,然后在析构函数阶段进行 unlock也可以避免这个问题。但是这样的类进行复制的时候
可能存在很多不合理的问题。
如果希望保存资源直到最后一个对象消失,并且在复制的时候是引用计数,可以使用上面这种情况。


对于第二个问题:

swap的 全or无:

struct PMImpl{ 
    std::tr1::shared_ptr<Image> bgImage;
    int imageChanges;
};
class PrettyMenu{
    ……
private:
    Mutex mutex;
    std::tr1::shared_ptr<PMImpl> pImpl;
};

void PrettyMenu::changeBackground(std::istream& imgSrc)
{
    using std::swap;//**条款**25
    Lock m1(&mutex);
    std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));
    pNew->bgImage.reset(new Image(imgSrc));;//修改副本
    ++pNew->imageChanges;
    swap(pImpl,pNew);
}

我们可以先弄一个副本,在进行完所有的操作之后,再与目标对象进行交换。
从而实现: 要不然就不处理,不然就全部处理完。
所以我们需要一个 不会抛出异常的swap操作。


参考:

《Effective C++》

posted @ 2017-03-27 21:26  Przz  阅读(429)  评论(0编辑  收藏  举报