c++11 多線程(4) condition_varible 總結(jié)


接著上節(jié) atomic,本節(jié)主要介紹condition_varible的內(nèi)容,練習(xí)代碼地址。本文參考http://www.cplusplus.com/reference/condition_variable/http://en.cppreference.com/w/cpp/header/condition_variable學(xué)習(xí)。

一、 <condition_variable>頭文件結(jié)構(gòu)

<condition_variable>頭文件主要包含了與條件變量相關(guān)的類和函數(shù)。主要含有類condition_variable和condition_variable_any,枚舉cv_status,函數(shù)notify_all_at_thread_exit。

Classes description
condition_variable 提供了std::unique_lock 相關(guān)聯(lián)的條件變量
condition_variable_any 提供與任何鎖類型相關(guān)聯(lián)的條件變量
Enum classes description
cv_status 列出在條件變量上限時(shí)等待的可能結(jié)果(枚舉)
Functions description
notify_all_at_thread_exit 當(dāng)這個(gè)線程完全完成(函數(shù))時(shí),調(diào)度調(diào)用notify_all來調(diào)用

結(jié)構(gòu)定義類似:

namespace std {
 
    class condition_variable;
    class condition_variable_any;
 
    void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
 
    enum class cv_status {
        no_timeout,
        timeout
    };
}

二、Classes

1. class condition_variable;

  • 條件變量類是一個(gè)同步原語,它可以用來阻塞一個(gè)線程或多個(gè)線程,直到另一個(gè)線程同時(shí)修改一個(gè)共享變量(條件),并通知條件變量。
  • 條件變量是能夠阻塞調(diào)用線程的對象,直到通知恢復(fù)為止。
    它使用unique_lock(在互斥鎖上)來鎖定線程,當(dāng)它的一個(gè)等待函數(shù)被調(diào)用時(shí)。線程仍然被阻塞,直到被另一個(gè)線程喚醒,該線程調(diào)用同一個(gè)條件變量對象上的通知函數(shù)。
  • 類型條件變量的對象總是使用unique_lock < mutex >等待:對于可以使用任何類型的可鎖定類型的選項(xiàng),參見condition_variable_any。
    示例1
// condition_variable example
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id (int id) {
  std::unique_lock<std::mutex> lck(mtx);
  while (!ready) cv.wait(lck);
  // ...
  std::cout << "thread " << id << '\n';
}

void go() {
  std::unique_lock<std::mutex> lck(mtx);
  ready = true;
  cv.notify_all();
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(print_id,i);

  std::cout << "10 threads ready to race...\n";
  go();                       // go!

  for (auto& th : threads) th.join();

  return 0;
}

示例2

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
 
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
 
void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});
 
    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";
 
    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";
 
    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}
 
int main()
{
    std::thread worker(worker_thread);
 
    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();
 
    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';
 
    worker.join();
    return 0;
}

1. Member functions

  1. (constructor)構(gòu)造函數(shù)
    定義:
       condition_variable();                                      default (1)
       condition_variable (const condition_variable&) = delete;   copy [deleted] (2)
       operator=   [deleted]

* 構(gòu)造一個(gè)condition_variable對象。
* 條件變量不能被復(fù)制/移動(dòng)(這個(gè)類型的拷貝構(gòu)造函數(shù)和賦值操作符都被刪除)。
* 如果構(gòu)建失敗,將拋出system_error異常, 根據(jù)庫的實(shí)現(xiàn),它可能會(huì)在其他情況下拋出異常。

exception type error condition description
system_error errc::resource_unavailable_try_again 資源限制(除了內(nèi)存分配)防止初始化
  1. (destructor)析構(gòu)函數(shù)
    * 破壞condition_variable對象。
    * 在此條件下被阻塞的任何線程在調(diào)用此析構(gòu)函數(shù)之前都將被通知。在這個(gè)析構(gòu)函數(shù)被調(diào)用之后,沒有線程將開始等待。
    注意
    1. 如果所有線程都被通知,調(diào)用析構(gòu)函數(shù)是安全的。它們不需要退出各自的等待函數(shù):一些線程可能還在等待重新獲得相關(guān)的鎖,或者等待調(diào)度在重新獲得它之后運(yùn)行。
    2.程序員必須確保在啟動(dòng)析構(gòu)函數(shù)時(shí),沒有線程試圖等待*this,特別是當(dāng)?shù)却€程調(diào)用循環(huán)中的等待函數(shù)或使用執(zhí)行謂詞的等待函數(shù)的重載時(shí)。

2. Wait functions

  1. std::condition_variable::wait
    void wait (unique_lock<mutex>& lck); unconditional (1)
    template <class Predicate> predicate (2)
    void wait (unique_lock<mutex>& lck, Predicate pred);
    1、 當(dāng)前線程的執(zhí)行(該線程將鎖定lck的互斥鎖)將被阻塞,直到被通知。
    2、 在阻塞線程的時(shí)刻,該函數(shù)將自動(dòng)調(diào)用lck.unlock(),允許其他鎖定的線程繼續(xù)運(yùn)行。
    3、 一旦通知(顯式地,通過其他線程),函數(shù)就unblock并調(diào)用lck. lock(),在調(diào)用函數(shù)時(shí)將lck留在相同的狀態(tài)。然后函數(shù)返回(注意,最后一個(gè)互斥鎖可能會(huì)在返回之前阻塞線程)。
    4、一般情況下,函數(shù)會(huì)被另一個(gè)線程的調(diào)用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實(shí)現(xiàn)可能產(chǎn)生虛假的喚醒調(diào)用,而不需要調(diào)用這些函數(shù)。因此,該函數(shù)的用戶將確保滿足恢復(fù)的條件。
    5、 定義(2)如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。
    類似這樣的實(shí)現(xiàn): while (!pred()) wait(lck);
    示例3
  // condition_variable::wait (with predicate)
  #include <iostream>           // std::cout
  #include <thread>             // std::thread, std::this_thread::yield
  #include <mutex>              // std::mutex, std::unique_lock
  #include <condition_variable> // std::condition_variable

  std::mutex mtx;
  std::condition_variable cv;

  int cargo = 0;
  bool shipment_available() {return cargo!=0;}

  void consume (int n) {
    for (int i=0; i<n; ++i) {
      std::unique_lock<std::mutex> lck(mtx);
      cv.wait(lck,shipment_available);
      // consume:
      std::cout << cargo << '\n';
      cargo=0;
    }
  }

  int main ()
  {
    std::thread consumer_thread (consume,10);

    // produce 10 items when needed:
    for (int i=0; i<10; ++i) {
      while (shipment_available()) std::this_thread::yield();
      std::unique_lock<std::mutex> lck(mtx);
      cargo = i+1;
      cv.notify_one();
    }

    consumer_thread.join();

    return 0;
  }

示例4

  #include <iostream>
  #include <condition_variable>
  #include <thread>
  #include <chrono>
 
  std::condition_variable cv;
  std::mutex cv_m; // This mutex is used for three purposes:
                 // 1) to synchronize accesses to i
                 // 2) to synchronize accesses to std::cerr
                 // 3) for the condition variable cv
  int i = 0;
 
  void waits()
  {
      std::unique_lock<std::mutex> lk(cv_m);
      std::cerr << "Waiting... \n";
      cv.wait(lk, []{return i == 1;});
      std::cerr << "...finished waiting. i == 1\n";
  }
 
  void signals()
  {
      std::this_thread::sleep_for(std::chrono::seconds(1));
      {
          std::lock_guard<std::mutex> lk(cv_m);
          std::cerr << "Notifying...\n";
      }
      cv.notify_all();
 
      std::this_thread::sleep_for(std::chrono::seconds(1));
      {
          std::lock_guard<std::mutex> lk(cv_m);
          i = 1;
          std::cerr << "Notifying again...\n";
      }
      cv.notify_all();
  }
 
  int main()
  {
      std::thread t1(waits), t2(waits), t3(waits), t4(signals);
      t1.join(); 
      t2.join(); 
      t3.join();
     t4.join();
      return 0;
  }
  1. std::condition_variable::wait_for
        (1) unconditional   
        template <class Rep, class Period>
            cv_status wait_for (unique_lock<mutex>& lck,
                      const chrono::duration<Rep,Period>& rel_time);
        (2) predicate   
        template <class Rep, class Period, class Predicate>
           bool wait_for (unique_lock<mutex>& lck,
                      const chrono::duration<Rep,Period>& rel_time, Predicate pred);

1、當(dāng)前執(zhí)行線程在rel_time時(shí)間內(nèi)或者在被通知之前(該線程將鎖定lck的互斥鎖)被阻塞(如果后者先發(fā)生的話)。
2、在阻塞線程的時(shí)刻,該函數(shù)將自動(dòng)調(diào)用lck.解鎖(),允許其他鎖定的線程繼續(xù)運(yùn)行。
3、一旦通知或一次rel_time時(shí)間段已經(jīng)過了,函數(shù)將會(huì)unblocks并調(diào)用lck. lock(),將lck與調(diào)用函數(shù)時(shí)的狀態(tài)保持一致。然后函數(shù)返回(注意,最后一個(gè)互斥鎖可能會(huì)在返回之前阻塞線程)。
4、一般情況下,函數(shù)會(huì)被另一個(gè)線程的調(diào)用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實(shí)現(xiàn)可能產(chǎn)生虛假的喚醒調(diào)用,而不需要調(diào)用這些函數(shù)。因此,該函數(shù)的用戶將確保滿足恢復(fù)的條件。
5、如果指定pred(定義2),如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。它的行為好像是:
return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));
示例5

  // condition_variable::wait_for example
  #include <iostream>           // std::cout
  #include <thread>             // std::thread
  #include <chrono>             // std::chrono::seconds
  #include <mutex>              // std::mutex, std::unique_lock
  #include <condition_variable> // std::condition_variable, std::cv_status

  std::condition_variable cv;

  int value;

  void read_value() {
    std::cin >> value;
    cv.notify_one();
  }

  int main ()
  {
    std::cout << "Please, enter an integer (I'll be printing dots): \n";
    std::thread th (read_value);

    std::mutex mtx;
    std::unique_lock<std::mutex> lck(mtx);
    while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
      std::cout << '.' << std::endl;
    }
    std::cout << "You entered: " << value << '\n';

    th.join();

    return 0;
  }

示例6

  #include <iostream>
  #include <atomic>
  #include <condition_variable>
  #include <thread>
  #include <chrono>
  //using namespace std::chrono_literals;
 
  std::condition_variable cv;
  std::mutex cv_m;
  int i;
 
  void waits(int idx)
  {
      std::unique_lock<std::mutex> lk(cv_m);
      if(cv.wait_for(lk, idx*std::chrono::milliseconds(100), []{return i == 1;})) 
          std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n';
      else
          std::cerr << "Thread " << idx << " timed out. i == " << i << '\n';
  }
 
  void signals()
  {
      std::this_thread::sleep_for(std::chrono::milliseconds(120));
      std::cerr << "Notifying...\n";
      cv.notify_all();
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
      {
          std::lock_guard<std::mutex> lk(cv_m);
          i = 1;
      }
      std::cerr << "Notifying again...\n";
      cv.notify_all();
  }
 
  int main()
  {
      std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
      t1.join(); t2.join(), t3.join(), t4.join();
      return 0;
  }
  1. std::condition_variable::wait_until
        (1) unconditional   
        template <class Clock, class Duration>
            cv_status wait_until (unique_lock<mutex>& lck,
                        const chrono::time_point<Clock,Duration>& abs_time);
        (2) predicate   
        template <class Clock, class Duration, class Predicate>
             bool wait_until (unique_lock<mutex>& lck,
                        const chrono::time_point<Clock,Duration>& abs_time,
                        Predicate pred);

1、當(dāng)前線程的執(zhí)行(該線程將鎖定lck的互斥鎖)會(huì)被阻塞,直到被通知或到達(dá)abs_time時(shí)間點(diǎn),無論那一個(gè)第一次發(fā)生。
2、在線程還處在阻塞時(shí),該函數(shù)將自動(dòng)調(diào)用lck.unlock(),允許其他鎖定的線程繼續(xù)運(yùn)行。
3、一旦被通知或一旦abs_time時(shí)間到了,函數(shù)就會(huì)接觸阻塞狀態(tài)并調(diào)用lck. lock()。在調(diào)用函數(shù)時(shí)將lck與函數(shù)的狀態(tài)保持在同一狀態(tài)。然后函數(shù)返回(注意,最后一個(gè)互斥鎖可能會(huì)在返回之前阻塞線程)。
4、一般情況下,函數(shù)會(huì)被另一個(gè)線程的調(diào)用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實(shí)現(xiàn)可能產(chǎn)生虛假的喚醒調(diào)用,而不需要調(diào)用這些函數(shù)。因此,該函數(shù)的使用者將要確保滿足恢復(fù)的條件。
5、(定義2)如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。它的行為好像是:

while (!pred())
      if ( wait_until(lck,abs_time) == cv_status::timeout)
          return pred();
return true;

示例7

  #include <iostream>
  #include <atomic>
  #include <condition_variable>
  #include <thread>
  #include <chrono>
  //using namespace std::chrono_literals;
 
  std::condition_variable cv;
  std::mutex cv_m;
  std::atomic<int> i{0};
 
  void waits(int idx)
  {
      std::unique_lock<std::mutex> lk(cv_m);
      auto now = std::chrono::system_clock::now();
      if(cv.wait_until(lk, now + idx*std::chrono::milliseconds(100), [](){return i == 1;}))
          std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n';
      else
          std::cerr << "Thread " << idx << " timed out. i == " << i << '\n';
  }
 
  void signals()
  {
      std::this_thread::sleep_for(std::chrono::milliseconds(120));
      std::cerr << "Notifying...\n";
      cv.notify_all();
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
      i = 1;
      std::cerr << "Notifying again...\n";
      cv.notify_all();
  }
 
  int main()
  {
      std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
      t1.join(); 
      t2.join();
      t3.join();
      t4.join();
      return 0;
  }

3. Notify functions

  1. std::condition_variable::notify_one
    void notify_one() noexcept; //定義
    1、打開當(dāng)前等待該條件的一個(gè)線程。如果沒有線程在等待,則該函數(shù)什么都不做。如果多于一個(gè),則不指定哪個(gè)線程被選中。
    2、該指令針對這個(gè)單獨(dú)的條件變量。這使得notify_one()不可能被延遲,并且在調(diào)用notify_one()之后才開始等待。
    3、通知線程不需要將鎖保存在與等待線程所持有的相同互斥鎖上;事實(shí)上,這樣做是一個(gè)悲觀的方法,因?yàn)橥ㄖ木€程會(huì)立即阻塞,等待通知線程釋放鎖。然而,一些實(shí)現(xiàn)(特別是許多pthreads實(shí)現(xiàn))識別這種情況,并避免這種“匆忙而等待”的場景,通過將等待的線程從條件變量的隊(duì)列直接傳遞到通知調(diào)用的互斥鎖的隊(duì)列中,而不將其喚醒。
    示例8
  // condition_variable::notify_one
  #include <iostream>           // std::cout
  #include <thread>             // std::thread
  #include <mutex>              // std::mutex, std::unique_lock
  #include <condition_variable> // std::condition_variable

  std::mutex mtx;
  std::condition_variable produce,consume;

  int cargo = 0;     // shared value by producers and consumers

  void consumer () {
    std::unique_lock<std::mutex> lck(mtx);
    while (cargo==0) consume.wait(lck);
    std::cout << cargo << '\n';
    cargo=0;
    produce.notify_one();
  }

  void producer (int id) {
    std::unique_lock<std::mutex> lck(mtx);
    while (cargo!=0) produce.wait(lck);
    cargo = id;
    consume.notify_one();
  }

  int main ()
  {
    std::thread consumers[10],producers[10];
    // spawn 10 consumers and 10 producers:
    for (int i=0; i<10; ++i) {
      consumers[i] = std::thread(consumer);
      producers[i] = std::thread(producer,i+1);
    }

    // join them back:
    for (int i=0; i<10; ++i) {
      producers[i].join();
      consumers[i].join();
    }

    return 0;
 }

示例9

  #include <iostream>
  #include <condition_variable>
  #include <thread>
  #include <chrono>
 
  std::condition_variable cv;
  std::mutex cv_m;
  int i = 0;
  bool done = false;
 
  void waits()
  {
      std::unique_lock<std::mutex> lk(cv_m);
      std::cout << "Waiting... \n";
      cv.wait(lk, []{return i == 1;});
      std::cout << "...finished waiting. i == 1\n";
      done = true;
  }
 
  void signals()
  {
      std::this_thread::sleep_for(std::chrono::seconds(1));
      std::cout << "Notifying falsely...\n";
      cv.notify_one(); // waiting thread is notified with i == 0.
                       // cv.wait wakes up, checks i, and goes back to waiting
 
      std::unique_lock<std::mutex> lk(cv_m);
      i  = 1;
      while (!done) 
      {
          std::cout << "Notifying true change...\n";
          lk.unlock();
          cv.notify_one(); // waiting thread is notified with i == 1, cv.wait     returns
          std::this_thread::sleep_for(std::chrono::seconds(1));
          lk.lock();
      }
  }
 
  int main()
  {
      std::thread t1(waits), t2(signals);
      t1.join(); 
      t2.join();
      return 0;
  }
  1. std::condition_variable::notify_all
    void notify_all() noexcept; // 定義
    解鎖當(dāng)前等待該條件的所有線程。如果沒有線程在等待,則該函數(shù)什么都不做。
    注意:
    1、notify_one()/notify_all()和wait()/wait_for()/wait_until() 三個(gè)原子部分中的每一個(gè)影響都在一個(gè)完全的順序(解鎖+等待, 喚醒, 鎖)中,可以被視為一個(gè)原子變量的修改順序:該命令是特定于這個(gè)單獨(dú)的條件變量的。這使得notify_one()不可能被延遲,并且在調(diào)用notify_one()之后才開始等待。
    2、通知線程不需要將鎖保存在與等待線程所持有的相同互斥鎖上;事實(shí)上,這樣做是一個(gè)悲觀的方法,因?yàn)橥ㄖ木€程會(huì)立即阻塞,等待通知線程釋放鎖。

    示例10
  // condition_variable::notify_all
  #include <iostream>           // std::cout
  #include <thread>             // std::thread
  #include <mutex>              // std::mutex, std::unique_lock
  #include <condition_variable> // std::condition_variable

  std::mutex mtx;
  std::condition_variable cv;
  bool ready = false;

  void print_id (int id) {
    std::unique_lock<std::mutex> lck(mtx);
    while (!ready) cv.wait(lck);
    // ...
    std::cout << "thread " << id << '\n';
  }

  void go() {
    std::unique_lock<std::mutex> lck(mtx);
    ready = true;
    cv.notify_all();
  }

  int main ()
  {
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
      threads[i] = std::thread(print_id,i);

    std::cout << "10 threads ready to race...\n";
    go();                       // go!

    for (auto& th : threads) th.join();

    return 0;
  }

示例11

  #include <iostream>
  #include <condition_variable>
  #include <thread>
  #include <chrono>
 
  std::condition_variable cv;
  std::mutex cv_m; // This mutex is used for three purposes:
                   // 1) to synchronize accesses to i
                   // 2) to synchronize accesses to std::cerr
                   // 3) for the condition variable cv
  int i = 0;
 
  void waits()
  {
      std::unique_lock<std::mutex> lk(cv_m);
      std::cerr << "Waiting... \n";
      cv.wait(lk, []{return i == 1;});
      std::cerr << "...finished waiting. i == 1\n";
  }
   
  void signals()
  {
      std::this_thread::sleep_for(std::chrono::seconds(1));
      {
          std::lock_guard<std::mutex> lk(cv_m);
          std::cerr << "Notifying...\n";
      }
      cv.notify_all();
 
      std::this_thread::sleep_for(std::chrono::seconds(1));
 
      {
          std::lock_guard<std::mutex> lk(cv_m);
          i = 1;
          std::cerr << "Notifying again...\n";
      }
      cv.notify_all();
  }
 
  int main()
  {
      std::thread t1(waits), t2(waits), t3(waits), t4(signals);
      t1.join(); 
      t2.join(); 
      t3.join();
      t4.join();
      return 0;
  }

2. class condition_variable_any;

1、與條件變量一樣,除了它的等待函數(shù)可以將任何可鎖類型作為參數(shù)(條件變量對象只能使用unique_lock < mutex >)。除此之外,它們是相同的。

2、std::condition_variable_any是std::condition_variable的泛化。而std::condition_variable 只能用于std::unique_lock < std::mutex >,condition_variable_any可以操作任何滿足基本要求的鎖。參見std::condition_變量,用于描述條件變量的語義。類std::condition_variable_any是一個(gè)標(biāo)準(zhǔn)布局類。它不可拷貝構(gòu)造的,移動(dòng)可構(gòu)造的,不可拷貝復(fù)制和移動(dòng)復(fù)制。

3、如果鎖是std::unique_lock,std::condition_variable可以提供更好的性能。
注意:
std::condition_variable_any與自定義的可鎖類型是用來提供方便的可中斷等待:自定義鎖操作會(huì)像預(yù)期的那樣鎖定相關(guān)的互斥對象,并且在接收到中斷信號時(shí)也執(zhí)行必要的設(shè)置來通知這個(gè)條件變量。

1. Member functions

  1. (constructor)構(gòu)造函數(shù)
    default (1) condition_variable_any();
    copy [deleted] (2) condition_variable_any (const condition_variable_any&) = delete;
    構(gòu)造std::condition_variable_any類型的對象. 不能通過拷貝和移動(dòng)構(gòu)造(這個(gè)類型的拷貝構(gòu)造函數(shù)和賦值操作符都被刪除)。

注意:
如果構(gòu)造失敗,會(huì)拋出一個(gè)系統(tǒng)異常:

根據(jù)庫的實(shí)現(xiàn),它可能會(huì)在其他情況下拋出異常如bad_alloc無法分配內(nèi)存。
operator= [deleted] 不允許復(fù)制。

  1. (destructor)析構(gòu)函數(shù)
    析構(gòu)condition_variable_any對象。在此條件下被阻塞的任何線程在調(diào)用此析構(gòu)函數(shù)之前都將被通知。在這個(gè)析構(gòu)函數(shù)被調(diào)用之后,沒有線程將開始等待。
    注意:
    1、 如果所有線程都被通知,調(diào)用析構(gòu)函數(shù)是安全的。它們不需要退出各自的等待函數(shù):一些線程可能還在等待重新獲得相關(guān)的鎖,或者等待調(diào)度在重新獲得它之后運(yùn)行。
    2、 程序員必須確保在啟動(dòng)析構(gòu)函數(shù)時(shí),沒有線程試圖等待,特別是當(dāng)?shù)却€程調(diào)用循環(huán)中的等待函數(shù)或使用執(zhí)行謂詞的等待函數(shù)的重載時(shí)。

2. Wait functions

  1. [std::condition_variable_any::wait]
    (http://www.cplusplus.com/reference/condition_variable/condition_variable_any/wait/)
        unconditional (1)   template <class Lock> void wait (Lock& lck);
        predicate (2)       template <class Lock, class Predicate>
                                  void wait (Lock& lck, Predicate pred);

1、當(dāng)前線程(當(dāng)前正在鎖定lck)的執(zhí)行將被阻止,直到通知為止。
2、在阻止線程的時(shí)刻,該函數(shù)自動(dòng)調(diào)用lck.unlock(),允許其他鎖定線程繼續(xù)。
3、一旦通知(顯式地,通過某個(gè)其他線程),函數(shù)解除阻塞和調(diào)用lck.lock(),使lck處于與調(diào)用函數(shù)時(shí)相同的狀態(tài)。然后函數(shù)返回(注意,這個(gè)最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數(shù)被通知通過另一個(gè)線程的呼叫喚醒成員notify_one 或 notify_all。但是某些實(shí)現(xiàn)可能會(huì)產(chǎn)生虛假的喚醒呼叫,而不會(huì)調(diào)用這些功能。因此,該功能的用戶應(yīng)確保其恢復(fù)條件得到滿足。
5、定義(2): 如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。表現(xiàn)為:

         while  (! pred ()) { 
              wait ( lock ); 
         }

示例12

  // condition_variable_any::wait (with predicate)
  #include <iostream>           // std::cout
  #include <thread>             // std::thread, std::this_thread::yield
  #include <mutex>              // std::mutex
  #include <condition_variable> // std::condition_variable_any

  std::mutex mtx;
  std::condition_variable_any cv;

  int cargo = 0;
  bool shipment_available() {return cargo!=0;}

  void consume (int n) {
    for (int i=0; i<n; ++i) {
      mtx.lock();
      cv.wait(mtx,shipment_available);
      // consume:
      std::cout << cargo << '\n';
      cargo=0;
      mtx.unlock();
    }
  }

  int main ()
  {
    std::thread consumer_thread (consume,10);

    // produce 10 items when needed:
    for (int i=0; i<10; ++i) {
      while (shipment_available()) std::this_thread::yield();
      mtx.lock();
      cargo = i+1;
      cv.notify_one();
      mtx.unlock();
    }

    consumer_thread.join();

    return 0;
  }

2.std::condition_variable_any::wait_for

       unconditional (1)    
            template <class Lock, class Rep, class Period>
                cv_status wait_for (Lock& lck,
                           const chrono::duration<Rep,Period>& rel_time);
       predicate (2)    
            template <class Lock, class Rep, class Period, class Predicate>
                 bool wait_for (Lock& lck,
                      const chrono::duration<Rep,Period>& rel_time, Predicate pred);

1、當(dāng)前線程(當(dāng)前鎖定lck)的執(zhí)行在rel_time期間被阻塞,或者直到通知(如果后者首先發(fā)生)。
2、在阻止線程的時(shí)刻,該函數(shù)自動(dòng)調(diào)用lck.unlock(),允許其他鎖定線程繼續(xù)。
3、一旦通知或一旦rel_time已經(jīng)過去,該函數(shù)解除阻塞和調(diào)用lck.lock(),使lck處于與調(diào)用函數(shù)時(shí)相同的狀態(tài)。然后函數(shù)返回(注意,這個(gè)最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數(shù)被通知通過另一個(gè)線程的呼叫喚醒成員notify_one 或 notify_one。但是某些實(shí)現(xiàn)可能會(huì)產(chǎn)生虛假的喚醒呼叫,而不會(huì)調(diào)用這些功能。因此,該功能的用戶應(yīng)確保其恢復(fù)條件得到滿足。
5、版本(2):如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)??梢岳斫鉃?/p>

       return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));

示例13

  // condition_variable_any::wait_for example
  #include <iostream>           // std::cout
  #include <thread>             // std::thread
  #include <chrono>             // std::chrono::seconds
  #include <mutex>              // std::mutex
  #include <condition_variable> // std::condition_variable_any,       std::cv_status

  std::condition_variable_any cv;

  int value;

  void read_value() {
    std::cin >> value;
    cv.notify_one();
  }

  int main ()
  {
    std::cout << "Please, enter an integer (I'll be printing dots): ";
    std::thread th (read_value);

    std::mutex mtx;
    mtx.lock();
    while   (cv.wait_for(mtx,std::chrono::seconds(1))==std::cv_status::timeout) {
      std::cout << '.';
    }
    std::cout << "You entered: " << value << '\n';
    mtx.unlock();

    th.join();

    return 0;
  }
  1. std::condition_variable_any::wait_until
       unconditional (1)    
       template <class Lock, class Clock, class Duration>
          cv_status wait_until (Lock& lck,
                                const chrono::time_point<Clock,Duration>& abs_time);
       predicate (2)    
       template <class Lock, class Clock, class Duration, class Predicate>
          bool wait_until (Lock& lck,
                           const chrono::time_point<Clock,Duration>& abs_time, 
                           Predicate pred);

1、當(dāng)前線程的執(zhí)行(當(dāng)前鎖定lck)將被阻塞,直到通知或直到abs_time,以先發(fā)生者為準(zhǔn)。
2、在阻止線程的時(shí)刻,該函數(shù)自動(dòng)調(diào)用lck.unlock(),允許其他鎖定線程繼續(xù)。
3、一旦通知或一旦為abs_time,函數(shù)解除阻塞和調(diào)用lck.lock(),使lck處于與調(diào)用函數(shù)時(shí)相同的狀態(tài)。然后函數(shù)返回(注意,這個(gè)最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數(shù)被通知通過另一個(gè)線程的呼叫喚醒成員notify_one 或會(huì)員 notify_one。但是某些實(shí)現(xiàn)可能會(huì)產(chǎn)生虛假的喚醒呼叫,而不會(huì)調(diào)用這些功能。因此,該功能的用戶應(yīng)確保其恢復(fù)條件得到滿足。
5、版本2:如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。表現(xiàn)為:

        while (!pred())
          if ( wait_until(lck,abs_time) == cv_status::timeout)
            return pred();
        return true;

示例14

#include <iostream>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <chrono>
//using namespace std::chrono_literals;
 
std::condition_variable cv;
std::mutex cv_m;
std::atomic<int> i{0};
 
void waits(int idx)  {
    std::unique_lock<std::mutex> lk(cv_m);
    auto now = std::chrono::system_clock::now();
    if(cv.wait_until(lk, now + idx*std::chrono::milliseconds(100), [](){return i == 1;}))
         std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n';
     else
         std::cerr << "Thread " << idx << " timed out. i == " << i << '\n';
}
 
void signals() {
    std::this_thread::sleep_for(std::chrono::milliseconds(120));
    std::cerr << "Notifying...\n";
    cv.notify_all();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    i = 1;
    std::cerr << "Notifying again...\n";
    cv.notify_all();
}
 
int main() {
    std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
    t1.join(); 
    t2.join();
    t3.join();
    t4.join();
    return 0;
}

3. Notify functions

  1. std::condition_variable_any::notify_one
    同上面std::condition_variable的函數(shù)。
    unblock當(dāng)前等待該條件的一個(gè)線程。如果沒有線程在等待,則該函數(shù)什么都不做。如果多于一個(gè),則不指定哪個(gè)線程被選中。
    示例15
  // condition_variable_any::notify_one
  #include <iostream>           // std::cout
  #include <thread>             // std::thread
  #include <mutex>              // std::mutex
  #include <condition_variable> // std::condition_variable_any

  std::mutex mtx;
  std::condition_variable_any cv;

  int cargo = 0;     // shared value by producers and consumers

 void consumer () {
      mtx.lock();
      while (cargo==0) cv.wait(mtx);
      std::cout << cargo << '\n';
      cargo=0;
      mtx.unlock();
  }

  void producer (int id) {
      mtx.lock();
      cargo = id;
      cv.notify_one();
      mtx.unlock();
  }
  
  int main ()
  {
    std::thread consumers[10],producers[10];

    // spawn 10 consumers and 10 producers:
    for (int i=0; i<10; ++i) {
      consumers[i] = std::thread(consumer);
      producers[i] = std::thread(producer,i+1);
    }

    // join them back:
    for (int i=0; i<10; ++i) {
      producers[i].join();
      consumers[i].join();
    }

    return 0;
  }

本例子在vs2013運(yùn)行結(jié)果 1-10,在ubuntu14.04,會(huì)出現(xiàn)死鎖等待情況。

  1. std::condition_variable_any::notify_all
    同上面std::condition_variable的函數(shù)。打開當(dāng)前等待該條件的所有線程。如果沒有線程在等待,則該函數(shù)什么都不做。
    示例16
  // condition_variable_any::notify_all
  #include <iostream>           // std::cout
  #include <thread>             // std::thread
  #include <mutex>              // std::mutex
  #include <condition_variable> // std::condition_variable_any

  std::mutex mtx;
  std::condition_variable_any cv;
  bool ready = false;

  void print_id (int id) {
    mtx.lock();
    while (!ready) cv.wait(mtx);
    // ...
    std::cout << "thread " << id << '\n';
    mtx.unlock();
  }

  void go() {
    mtx.lock();
    ready = true;
    cv.notify_all();
    mtx.unlock();
  }

  int main ()
  {
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
      threads[i] = std::thread(print_id,i);

    std::cout << "10 threads ready to race...\n";
    go();                       // go!

    for (auto& th : threads) th.join();

    return 0;
  }

三、 Enum classes

1. std::cv_status

    enum class cv_status;   定義

條件變量狀態(tài):
表示是否由于超時(shí)而返回的函數(shù)。
這種類型的值由condition_variable和condition_variable_any的成員wait_for和wait_until返回。
定義類似:

 enum class cv_status { no_timeout, timeout };
cv_status::no_timeout :  該函數(shù)返回沒有超時(shí)
cv_status::timeout : 該函數(shù)由于達(dá)到其時(shí)間限制(timeout)而返回。

四、Functions

1. std::notify_all_at_thread_exit

   void notify_all_at_thread_exit (condition_variable& cond,unique_lock<mutex> lck);

當(dāng)調(diào)用線程退出時(shí),等待在cond上的所有線程都被通知恢復(fù)執(zhí)行。
該函數(shù)還獲得由lck管理的mutex對象上的鎖的所有權(quán),該對象在內(nèi)部由函數(shù)存儲(chǔ),并在線程退出時(shí)解鎖(僅在通知所有線程之前),行為如下:一旦所有具有線程存儲(chǔ)時(shí)間的對象都被銷毀:1 lck.unlock(); 2 cond.notify_all();
注意
如果lock. mutex()沒有被當(dāng)前線程鎖定,則調(diào)用此函數(shù)是未定義的行為。
如果lock. mutex()與當(dāng)前在同一條件變量中等待的所有其他線程使用的互斥對象不相同,則調(diào)用此函數(shù)。
所提供的鎖一直保持到線程退出。一旦調(diào)用這個(gè)函數(shù),就不會(huì)有更多的線程可能獲得相同的鎖以等待cond。如果某個(gè)線程在這個(gè)條件變量上等待,它不應(yīng)該嘗試釋放并重新獲取鎖,當(dāng)它被錯(cuò)誤地喚醒時(shí)。
在典型的用例中,這個(gè)函數(shù)是由一個(gè)獨(dú)立的線程調(diào)用的最后一個(gè)函數(shù)。

示例17

  // notify_all_at_thread_exit
  #include <iostream>           // std::cout
  #include <thread>             // std::thread
  #include <mutex>              // std::mutex, std::unique_lock
  #include <condition_variable> // std::condition_variable

  std::mutex mtx;
  std::condition_variable cv;
  bool ready = false;

  void print_id (int id) {
    std::unique_lock<std::mutex> lck(mtx);
    while (!ready) cv.wait(lck);
    // ...
    std::cout << "thread " << id << '\n';
  }

  void go() {
    std::unique_lock<std::mutex> lck(mtx);
    std::notify_all_at_thread_exit(cv,std::move(lck));
    ready = true;
  }

  int main ()
  {
    std::thread threads[10];
    // spawn 10 threads:
    for (int i=0; i<10; ++i)
      threads[i] = std::thread(print_id,i);
    std::cout << "10 threads ready to race...\n";

    std::thread(go).detach();   // go!

    for (auto& th : threads) th.join();

    return 0;
  }

上面的例子ubuntu14.04 g++4.8.4 編譯時(shí)報(bào)錯(cuò)notify_all_at_thread_exit 不是 std的成語函數(shù); 原因:/usr/include/c++/4.8/condition_variable文件中沒有對notify_all_at_thread_exit函數(shù)進(jìn)行定義; vs2013中VC的include下的 condition_variable有定義,所以使用vs2013編譯運(yùn)行正常。

到這里condition_varible 頭文件中的內(nèi)容已經(jīng)列完。 下一篇 c++11 多線程(5) future 總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 接著上上節(jié) thread ,本節(jié)主要介紹mutex的內(nèi)容,練習(xí)代碼地址。<mutex>:該頭文件主要聲明了與互斥量...
    jorion閱讀 12,683評論 2 4
  • <condition_variable > 頭文件主要包含了與條件變量相關(guān)的類和函數(shù)。相關(guān)的類包括 std::co...
    張霸天閱讀 3,886評論 1 0
  • 本文主要是針對C++中多線程并發(fā)操作參見(cplusplus)進(jìn)行解釋,文章從下面幾個(gè)方面進(jìn)行學(xué)習(xí),分別介紹多線程...
    jorion閱讀 9,975評論 0 10
  • 接著上節(jié) mutex,本節(jié)主要介紹atomic的內(nèi)容,練習(xí)代碼地址。本文參考http://www.cplusplu...
    jorion閱讀 74,074評論 1 14
  • 本文根據(jù)眾多互聯(lián)網(wǎng)博客內(nèi)容整理后形成,引用內(nèi)容的版權(quán)歸原始作者所有,僅限于學(xué)習(xí)研究使用,不得用于任何商業(yè)用途。 互...
    深紅的眼眸閱讀 1,221評論 0 0

友情鏈接更多精彩內(nèi)容