最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

最新:5. 使用互斥量保护共享数据

来源:博客园


(资料图片仅供参考)

使用互斥量保护共享数据

  1. C++中使用互斥量
    • C++通过实例化std::mutex创建互斥量,通过调用成员函数lock()进行加锁,unlock()进行解锁,在实践中不推荐直接调用成员函数,因为调用成员函数就意味着,必须记住在每个函数的出口都需要调用unlock(),同时包括异常的情况,C++中推荐使用lock_guard实例对数据进行保护 ,lock_guardmutex都在头文件中进行声明
    #include#includestd::listli;std::mutex li_mutex;void add_to_list(int num){std::lock_guardguard(li_mutex);li.push_back(num);}bool list_contain(int value_to_find){std::lock_guardguard(li_mutex);return std::find(li.begin(),li.end(),value_to_find) != li.end();}
    上述代码中使用全局变量进行保护,通常情况下没有问题,但是大多情况下,互斥量和保护的数据放在同一个类中。当一个成员函数返回的是保护数据的指针的或者引用的时候,会破坏数据的保护,具有访问能力的指针或者引用可以访问或者修改被保护的数据,而不会被互斥锁限制,因此在设计互斥量的时候需要能够锁住任何数据的访问方式,不留后门
  1. 精心组织代码来保护共享数据
    • 在确保成员函数不会传出指针或者引用的同时,检查成员函数是否通过指针或者引用的方式了来调用同样重要,函数可能在没有互斥量保护的地方存储这指针或者引用,例如如下代码:
    class foo{ private: int data;mutex _mutex; public: templatevoid dowork(Function func){lock_guardg(_mutex);func(data);}}int * danger_pointer;void danger_func(int & tmp){danger_pointer = &tmp;}int main(){foo f;f.dowork(danger_func);}
    上述代码中,虽然有互斥锁的保护,但是foo中的数据依旧通过指针被传递给了danger_pointer,切记不要将受保护的数据的指针或者 引用传递到互斥锁的作用和与之外
  1. 发现接口内在的条件竞争
    • 元素操作在多线程开发中,对于共享资源存在竞争条件,例如上一个线程可能对栈进行了某种判别条件,但是时间片很快切给了下一个线程,可能会导致上一个进程的判别条件发生改变,但是程序不会重复执行判别代码,如下;
    stacksta;void process_1(){if(sta.empty()){...}}void process_2(){sta.push(...);}int main(){thread t1(process_1);thread t2(process_2);t1.join();t2.join();}
    在上述例子中,可能第一个进程刚 判断了栈为空,还没进行下面的操作,第二个线程就向栈中添加了元素,第一个线程的判断条件将不在成立,但是仍然会进行下面的操作。
  1. 多线程中的元素转移问题举个例子,假设有一个stack>,当vector中有大量元素的时候,拷贝其中的内容可能会发生bad_alloc的异常,如果此时调用stack中的pop方法,可能造成栈顶元素确实从栈中弹出,但是并没有足够的空间对其进行拷贝,这个时候,有就会丢失,基本的解决办法就是先拷贝,再弹出,这样又会产生空间不足的问题。针对这种问题,有如下几个方法可以进行解决。
    • 传入一个引用将变量的引用作为参数传递到pop函数当中
    std::vectorres;sta.pop(res);此处pop函数具体如下:void pop(vector& res){lock_guardloc(m);if(sta.empty())throw empty_stack();res = data.top();data.pop();}
    • 返回弹出值的指针返回指针和返回引用的原理是一样的
    auto ptr = sta.pop();此处pop函数的具体实现如下:std::shared_ptr pop(){std::loc_guardloc(m);if(data.empty()) throw empty_stack();shared_ptrconst res(std::make_shared(data.top()));return res;}
  1. 死锁问题死锁产生的四个必要条件分别为1. 互斥条件 2. 不可剥夺条件 3. 请求和保持条件 4. 循环等待条件

互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。以上给出了导致死锁的四个必要条件,只要系统发生死锁则以上四个条件至少有一个成立。事实上循环等待的成立蕴含了前三个条件的成立,似乎没有必要列出然而考虑这些条件对死锁的预防是有利的,因为可以通过破坏四个条件中的任何一个来预防死锁的发生。————————————————版权声明:本文为CSDN博主「Hyacinth_Dy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/jyy305/article/details/70077042

死锁的避免方法如下:

  • 避免嵌套所
  • 避免在持有锁的时候调用用户提供的代码
  • 按照一定的顺序上锁
  • 使用锁的层次结构对于这个方法,他的意思是对每个锁进行级别标注,当低层次的锁被锁上的时候,高层次的锁不允许上锁,原理类似于按一定的顺序上锁

关键词: 成员函数 判别条件 等待条件