c++并发编程实战 第三章

           ·使用互斥量保护共享数据:任何一个线程在执行这些代码时,其他任何线程试图访问共享数据结构,就必须等到那一段代码执行结束。于是,一个线程就不可能会看到被破坏的不变量,除非它本身就是修改共享数据的线程。

           ·std::mutex:当访问共享数据前,使用互斥量将相关数据锁住,再当访问结束后,再将数据解锁。线程库需要保证,当一个线程使用特定互斥量锁住共享数据时,其他的线程想要访问锁住的数据,都必须等到之前那个线程对数据进行解锁后,才能进行访问。这就保证了所有线程能看到共享数据,而不破坏不变量。

互斥量和要保护的数据,在类中都需要定义为private成员,这会让访问数据的代码变的清晰,并且容易看出在什么时候对互斥量上锁。当所有成员函数都会在调用时对数据上锁,结束时对数据解锁,那么就保证了数据访问时不变量不被破坏。当然,也不是总是那么理想,聪明的你一定注意到了:当其中一个成员函数返回的是保护数据的指针或引用时,会破坏对数据的保护。具有访问能力的指针或引用可以访问(并可能修改)被保护的数据,而不会被互斥锁限制。

Stack:后入先出

c++并发编程实战 第三章_第1张图片

  • push()会将一个元素放入stack中。
  • top()会返回stack中的栈顶元素,返回的是reference,可以就地修改值。
  • pop()移除栈顶元素,无返回值。
  • size()返回stack长度。
  • empty()返回stack是否为空。

 

·死锁:与条件竞争完全相反——不同的两个线程会互相等待,从而什么都没做。

·每个锁只锁一个唯一共享资源:这样,才能保证锁应用的单一,也能更好的确保加锁的范围尽 量小。 对于共享全局资源,应该根据实际需要,每类或每个资源,有一把锁。

·避免嵌套加锁:如果必须加锁,务必保证不同地方的加锁顺序是一样的

·锁中避免使用跳转语句 

 

#include 
#include 
#include 
#include 
class dns_entry;
class dns_cache
{
    std::map entries;
    mutable boost::shared_mutex entry_mutex;

public:
    dns_entry find_entry(std::string const& domain) const
    {
        boost::shared_lock lk(entry_mutex); //1
        std::map::const_iterator const it=
            entries.find(domain);
        return (it==entries.end())?dns_entry():it->second;
    }

    void update_or_add_entry(std::string const& domain, dns_entry const& dns_details)
    {
        std::lock_guard lk(entry_mutex); // 2
        entries[domain]=dns_details;
    }
};

清单中,find_entry()使用 boost::shared_lock<>  来保护共享和只读权限①;这就使得多线程可以同时调用find_entry(),且不会出错。另一方面,update_or_add_entry()使用 std::lock_guard<>  实例,当表格需要更新时②,为其提供独占访问权限;update_or_add_entry()函数调用时,独占锁会阻止其他线程对数据结构进行修改,并且阻止线程调用find_entry()。

 

本章总结

本章讨论了当两个线程间的共享数据发生恶性条件竞争会带来多么严重的灾难,还讨论了如何使用 std::mutex  ,和如何避免这些问题。如你所见,互斥量并不是灵丹妙药,其还有自己的问题(比如:死锁),虽然C++标准库提供了一类工具来避免这些(例如: std::lock()  )。你还见识了一些用于避免死锁的先进技术,之后了解了锁所有权的转移,以及一些围绕如何选取适当粒度锁产生的问题。最后,讨论了在具体情况下,数据保护的替代方案,例如: std::call_once()  和 boost::shared_mutex  。还有一个方面没有涉及到,那就是等待其他线程作为输入的情况。我们的线程安全栈,仅是在栈为空时,抛出一个异常,所以当一个线程要等待其他线程向栈压入一个值时(这是一个线程安全栈的主要用途之一),它不得不多次尝试去弹出一个值,当捕获抛出的异常时,再次进行尝试。这种消耗资源的检查,没有任何意义。并且,不断的检查会影响系统中其他线程的运行,这反而会妨碍程序的进展。我们需要一些方法让一个线程等待其他线程完成任务,但在等待过程中不占用CPU。第4章中,会去建立一些工具,用于保护共享数据,还会介绍一些线程同步操作的机制;第6章中,如何构建更大型的可复用的数据类型。

你可能感兴趣的:(并发编程学习)