天天看点

C++多线程(mutex类)(三)

mutex 类

详细方法介绍参见C++ Ref

  • Mutex Types

    Mutex types是lockable type类型,用于保护对特定区域代码的访问。支持操作:lock 和unlock。lock操作用于防止其他thread的访问,unlock操作用于解除lock。C++11中有4种Mutex Type

    • mutex
      • 构造器 constexpr mutex() noexcept; 不支持拷贝构造或者赋值构造
      • 支持lock(),try_lock(),unlock(),native_handle()方法
    • recursive_mutex
      • 与mutex相比,其lock()与try_lock()可以对同一recursive_mutex对象获得多级ownership
    • timed_mutex
      • 额外提供try_lock_for()与try_lock_until()方法。两个方法的含义是在timed_mutex对象被其他线程lock住时,当前线程将会block for duration or to timepoint
    • recursive_timed_mutex 集成了recursive_mutex和timed_mutex特性

lock方法执行区别

- 对任意的mutex type对象,若其(cur_mutex)已被其他thread locked,则调用lock()方法将会进入block,调用try_lock()方法将会失败,返回false;调用try_lock_for/try_lock_until()方法将会block一段时间,在此期间一直等待获取cur_mutex的lock

- 若cur_mutex还没有被任意thread获取lock,则所有lock方法将会获得lock并返回

- 若cur_mutex已被当前thread获取lock,对于mutex对象和timed_mutex对象,调用lock(),try_lock(),try_lock_for(),try_lock_until() 会产生死锁,函数有的会产生Error,有的不会; 若对recursive_mutex和recursive_timed_mutex调用lock(),try_lock(),try_lock_for(),try_lock_until(),将会产生新一级的lock,最后unlock时也需要调用相同数目的unlock方法

  • Locks

    Locks提供了一种管理mutex的机制

    • lock_guard
      • 是一种简单的机制,仅提供构造器和析构器方法
      • 创建时自动上锁,lock_guard析构时自动调用unlock方法
    • unique_lock
      • 相对lock_guard来说,unique_lock更加灵活。创建时自动对mutex对象上锁,析构时自动调用unlock()方法。unique_lock并不管理mutex对象的生命周期,但是mutex对象在lock_guard析构时必须存在。
      • 支持多种形式的构造器,包括初始化以及移动构造
      • 支持lock(),try_lock(),try_lock_for(),try_lock_until(),unlock()方法
      • 支持移动赋值 unique_lock& operator= (unique_lock&& x) noexcept;
      • swap()方法:交换管理的mutex对象和owning state(lock or unlock)
      • release()方法:mutex_type* release() noexcept; 返回指向管理的mutex_type的指针,并且unique_lock释放对mutex 对象的管理权,此后unique_lock不再管理任何mutex 对象
      • owns_lock():返回是否lock住某个mutex对象
      • (): 与owns_lock()功能相同
      • mutex(): 返回指向管理的mutex对象的指针
  • Other Type
  • Functions
    • lock()
      • template <class Mutex1, class Mutex2, class… Mutexes>

        void lock (Mutex1& a, Mutex2& b, Mutexes&… cde);

      • lock 所有Mutex对象,但并不按照特定顺序,若暂时无法lock则将block调用线程
      • 函数返回时保证所有Mutex对象均以被lock,且无死锁产生
      • 若由于exception导致无法lock某Mutex对象,函数在fail前将首先unlock所有已被lock的Mutex对象
    • try_lock()
      • template <class Mutex1, class Mutex2, class… Mutexes> int try_lock (Mutex1& a, Mutex2& b, Mutexes&… cde);
      • 按照顺序依次尝试lock Mutex对象a,b,…,cde直到所有Mutex对象均lock成功或者任一Mutex 对象lock失败(return false or throw exception)。若失败,则将会对所有已经成功lock的Mutex对象执行unlock操作
    • call_once()
      • template <class Fn, class… Args>

        void call_once (once_flag& flag, Fn&& fn, Args&&… args);

      • 该函数为Passive调用,任意线程调用任意fn,若flag相同,则仅做一次调用。

        call_once实例

#include <iostream>       // std::cout
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::milliseconds
#include <mutex>          // std::call_once, std::once_flag
using namespace::std;
int winner;
void set_winner(int x) { cout << "Enter into set_winner" << endl; winner = x; }//仅执行一次
void print_winner(int x) { cout << "Enter into print_winner" << endl; winner = x; }
std::once_flag winner_flag;

void wait_1000ms(int id) {
    // count to 1000, waiting 1ms between increments:
    for (int i = ; i<; ++i)
        std::this_thread::sleep_for(std::chrono::milliseconds());
    // claim to be the winner (only the first such call_once on winner_flag is executed):
    std::call_once(winner_flag, set_winner, id);
    std::call_once(winner_flag, print_winner, );//will not execute since winner_flag has been called by call_once
}


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

    std::cout << "waiting for the first among 10 threads to count 1000 ms...\n";

    for (auto& th : threads) th.join();
    std::cout << "winner thread: " << winner << '\n';
    this_thread::sleep_for(chrono::seconds());
    cout << "call_once after Sleep" <<endl;
    thread t(wait_1000ms, );
    t.join();
    cout << "New winner " << winner << endl;//the winner result are always same as before
    system("pause");
    return ;
}
           

可能的运行结果

waiting for the first among  threads to count  ms...
Enter into set_winner
winner thread: 
call_once after Sleep
New winner 
Press any key to continue . . .
           

继续阅读