返回值的锁

来源:互联网 发布:中国网络远程学校 编辑:程序博客网 时间:2024/05/29 15:13

    在多线程编程中,时刻需要注意加锁,这也是多线程编程中的一个难点。

    但是我们所用到的锁都是“协议锁”,即是一个“君子协定”,所谓“防君子不防小人”。这也是多线程编程中比较让人肾疼的地方。

    我觉得用“锁”这个名词对编程中的这种操作现象进行描述其实是不太合适的,反而会引起歧义,把它看成是门口挂的一块“告示牌”会更合适一些 ——— 一面写着“有人勿进”,一面写着“空闲可用”。

    你的线程作为一个“君子”,在到达访问临界资源的门(门上是没有锁的,也没办法给它装个锁)前,首先要看看这块告示牌,如果写着的是“有人勿进”,那么你就在门口等着,时不时再瞄一眼,看告示牌是不是翻了个面了,也可能是在你等得快睡着的时候被人叫醒。

    否则你就可以进去,顺便把优雅的将告示牌翻个面。

    等你从门里出来的时候,或许你可以看见门口也等了一大堆像你一样的君子了,那么就看你是默默翻转告示牌离开,还是和他们打声招呼了。

    如果所有的人都像你这么君子作风,那么应该没什么问题了,井然有序,虽然可能办事效率会很低啦。然而总保不准会有一两个非君子的小人,进门不看告示牌,不管三七二十一,冲进去拿了东西就走,还不顺手把门口的告示牌给翻一下。

    当然如果他进门的时候刚好是“空闲可用”状态,或者即便是有人,但他拿的东西也不是别人想要的,那么顶多也就是虚惊一场。怕就怕的是起了冲突,那么很不幸,你的软件可能就要崩溃了。。


下面说说我遇到的比较肾疼的问题:

    接手的项目实在是惨不忍睹。有太多诸如定义了一个单例类对象,必然是需要当多个线程访问的临界资源的,然而却提供着 static MyCls& MyCls::GetMyCls();这种借口。然而本来应该提供的需要在内部加锁的“增删改查”接口可能却压根没有!!!

    如果只是一个类这样写还好,自己重构一下问题不大,然而当整个项目几乎都这样的时候,我也无能为力。

    面对偶发并且频率极高的崩溃,也只能死马当活马医,加锁吧。。。

    需要在定义这个类的模块中增加锁成员,并且导出出去供其他模块使用,多么肾疼的写法。。。

    好吧,只好用C语言强大万能的宏来包装一下,搞一个返回值的锁吧。

    代码如下:

    1. 封装锁:

//CriticalSection_Lock.h#pragma once#include <windows.h>class CriticalSection_Lock{public:    CriticalSection_Lock(void);    ~CriticalSection_Lock(void);    //{重载new delete解决跨模块的析构    void *operator new(size_t size){return ::operator new(size);}    void *operator new[](size_t size){return ::operator new(size);}    void operator delete(void *p){return ::operator delete(p);}    void operator delete[](void *p){return ::operator delete(p);}    //}}private:    static CRITICAL_SECTION *m_cs;};#define CS_LOCK(name)   { CriticalSection_Lock lock_##name;#define CS_UNLOCK(name) }
//CriticalSection_Lock.cpp#include "CriticalSection_Lock.h"CRITICAL_SECTION *CriticalSection_Lock::m_cs = nullptr;CriticalSection_Lock::CriticalSection_Lock(void){    if (nullptr == m_cs)    {        static CRITICAL_SECTION cs;//实际的代码不应该这样写,也不是这么写的,这里只是给个简单的例子,用了一个全局的锁。不过即便像这样的示例代码,也是有问题的,需要使用双检锁避免创建多个cs实例(如果真的需要使用全局单例的话)        m_cs = &cs;        InitializeCriticalSectionEx(m_cs,4000,0);    }    EnterCriticalSection(m_cs);}CriticalSection_Lock::~CriticalSection_Lock(void){    LeaveCriticalSection(m_cs);}
    2.返回值锁相关的宏:
//RetValueLock.h#pragma once#define RETURN_NEED_LOCK#ifdef RETURN_NEED_LOCK#define RV_WITH_LOCK_DECL(Type)                                                                 \struct RetWithLock##Type                                                                        \{                                                                                               \    RetWithLock##Type(Type type, std::shared_ptr<CriticalSection_Lock> sp)                      \        :m_type(type)                                                                           \        ,m_sp(sp){}                                                                             \    Type m_type;                                                                                \    std::shared_ptr<CriticalSection_Lock> m_sp;                                                 \};#define RV_WITH_LOCK(Type) RetWithLock##Type#define RETURN_RET_WITH_LOCK(Type,name)                                                         \    CriticalSection_Lock *pcs = new CriticalSection_Lock;                                       \    std::shared_ptr<CriticalSection_Lock> spint(pcs);                                           \    return RetWithLock##Type(name,spint);#define GET_REAL_RET_FROM_LOCKRET(Name) Name.m_type#else#define RV_WITH_LOCK_DECL(Type)#define RV_WITH_LOCK(Type) Type#define RETURN_RET_WITH_LOCK(Type,name) return name;#define GET_REAL_RET_FROM_LOCKRET(Name) Name#endif
    3.使用:
//1.typedef成一个可以放进标识符的名字typedef MyCls& MyClsRef;//2.替换GetMyCls();等申明和实现//MyCls& MyCls::GetMyCls()RV_WITH_LOCK(MyClsRef) MyCls::GetMyCls(){    //....}//3.替换外部调用GetMyCls()的地方///MyCls& v = MyCls::GetMyCls();RV_WITH_LOCK(MyClsRef) lv = GetVct();MyClsRef v = GET_REAL_RET_FROM_LOCKRET(lv);

    然后基本上几个简单的正则替换就搞定了,崩溃是没有了,然而至于性能,呵呵,管他呢,毕竟我只是个擦屁股的,我还能奢望我自己做得有多好呢[笑哭脸]。。。

0 0
原创粉丝点击