C++11 多线程编程2

  • 用作thread_note的补充,主要以Cpp-Concurrency-In-Action 为例

3 线程间共享数据

  • 保护很少更新的数据结构

  • 一个作者线程独占访问,多个读者线程并发访问

  • 频率很低的数据要更新,需要一个写线程独占互斥量,多个读线程共享互斥量

  • 1
    2
    3
    (1)当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞。(交叉互斥)
    (2)当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行加锁的线程将会被阻塞。(读共享,交叉互斥)
    (3)当读写锁在读模式的锁状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求则长期阻塞。(写优先)
    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class  dns_entry{};
      class dns_cache{
      map<string,dns_entry> entries;
      mutable shared_mutex entry_mutex;
      public:
      dns_entry find_entry(string const& domain) const//读线程
      {
      shared_lock<shared_mutex> lk(entry_mutex);
      map<string,dns_entry>::const_iterator const it=
      entries.find(domain);
      return (it==entries.end())? dns_entry():it->second;
      }
      void update_or_add_entry(string const& domain,dns_entry const& dns_detail)
      {
      lock_guard<shared_mutex> lk(entry_mutex);
      entries[domain]=dns_detail;
      }
      }

4 同步并发

  • 等待条件完成

  • 必须要用unique_lock,不能用Lock_guard,因为wait时,可能会解锁

  • wait 先检查条件,如果满足,直接返回。否则,解锁,处于等待状态。

  • 当被唤醒时,加锁,检查条件,如果条件不满足,解锁,继续睡眠,条件满足,返回

  • 虚假唤醒:被唤醒,但是条件不满足

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    std::mutex mut;
    std::queue<data_chunk> data_queue; // 1
    std::condition_variable data_cond;
    void data_preparation_thread()
    {
    while(more_data_to_prepare())
    {
    data_chunk const data=prepare_data();
    std::lock_guard<std::mutex> lk(mut);
    data_queue.push(data); // 2
    data_cond.notify_one(); // 3
    }
    }
    void data_processing_thread()
    {
    while(true)
    {
    std::unique_lock<std::mutex> lk(mut); // 4
    data_cond.wait(
    lk,[]{return !data_queue.empty();}); // 5
    data_chunk data=data_queue.front();
    data_queue.pop();
    lk.unlock(); // 6
    process(data);
    if(is_last_chunk(data))
    break;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33

    struct X
    {
    void foo(int,std::string const&);
    std::string bar(std::string const&);
    };
    X x;
    auto f1=std::async(&X::foo,&x,42,"hello"); // 调用p->foo(42,"hello"),p是指向x的指针
    auto f2=std::async(&X::bar,x,"goodbye"); // 调用tmpx.bar("goodbye"), tmpx是x的拷贝副本
    struct Y
    {
    double operator()(double);
    };
    Y y;
    auto f3=std::async(Y(),3.141); // 调用tmpy(3.141),tmpy通过Y的移动构造函数得到
    auto f4=std::async(std::ref(y),2.718); // 调用y(2.718)

    X baz(X& test)
    {}
    std::async(baz,std::ref(x)); // 调用baz(x)
    class move_only
    {
    public:
    move_only();
    move_only(move_only&&)
    move_only(move_only const&) = delete;
    move_only& operator=(move_only&&);
    move_only& operator=(move_only const&) = delete;
    void operator()();
    };
    auto f5=std::async(move_only()); // 调用tmp(),tmp是通过
    std::move(move_only())构造得到
    //当参数为右值(rvalues)时,拷贝操作将使用移动的方式转移原始数据
  • wait_for wait_until

    • std::this_thread namespace sleep_for(duration) sleep_until (time_point) N/A
      std::condition_ variable or std::condition_ variable_any wait_for(lock, duration) wait_until(lock, time_point) std::cv_status:: timeout or std::cv_status:: no_timeout
      wait_for(lock, duration, predicate) wait_until(lock, time_point, predicate) bool—the return value of the predicate when awakened
      std::timed_mutex or std::recursive_ timed_mutex try_lock_for (duration) try_lock_until (time_point) bool—true if the lock was acquired, false otherwise
      std::unique_ lock unique_lock(lockable, duration) unique_lock(lockable, time_point) N/A—owns_lock() on the newly constructed object; returns true if the lock was acquired, false otherwise
      try_lock_for(duration) try_lock_until (time_point) bool—true if the lock was acquired, false otherwise
      std::future or std::shared_ future wait_for(duration) wait_until (time_point) std::future_status:: timeout if the wait timed out, std::future_ status::ready if the future is ready, or std::future_status:: deferred if the future holds a deferred function that hasn’t yet started
文章目录
  1. 1. 3 线程间共享数据
  2. 2. 4 同步并发