前言
之前的工作項目基本不使用多線程,一直對多線程的理解比較淺顯,一般應用也是主從兩個線程,也不涉及資源鎖,以及其他的各種鎖,信號量之類的,更別提線程池之類的,這次也特意學習記錄一下多線程。
庫知識
C++11現在也有了自己的多線程庫,從C++11的線程庫開始學習瞭解。
庫主要分爲:
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <future>
std::thread
std::thread類,主要用來創建創建線程,對線程對象進行相關操作,控制線程的生命週期。
std::thread 類成員函數主要如下:
//構造函數
thread() noexcept = default;
thread(thread&) = delete;
thread(const thread&) = delete;
thread(const thread&&) = delete;
thread(thread&& __t) noexcept { swap(__t); }
//析構函數
~thread()
{
if (joinable())
std::terminate();
}
//交換函數,用來交換底層句柄
void swap(thread& __t) noexcept { std::swap(_M_id, __t._M_id); }
//join狀態函數,判斷線程是否能被線程控制
bool joinable() const noexcept { return !(_M_id == id()); } 、
//join 等待線程執行結束
void join();
//線程分離函數
void detach();
//得到線程ID
thread::id get_id() const noexcept { return _M_id; }
//native_handle_type 得到與操作系統相關的原生線程句柄
typedef __gthread_t native_handle_type;
native_handle_type native_handle() { return _M_id.M_thread; }
//hardware_concurrency 獲得當前程序最大支持的線程數,多線程一般代表系統核數
static unsigned int hardware_concurrency() noexcept;
std::mutex
互斥鎖,主要用來線程同步,保證在同一時間內只有一個線程對某一資源進行讀寫操作。
std::mutex 類主要有以下幾種類,mutex,recursive_mutex,timed_mutex,recursive_timed_mutex幾種類。
mutex
基礎類:
//加鎖
void lock();
//解鎖
void unlock();
//嘗試鎖
bool try_lock();
recursive_mutex
遞歸鎖:允許在同一個線程內,多一個互斥量進行多次請求。即在同一個線程內,多次獲取鎖定同一個遞歸鎖,且不會產生死鎖。
//構造函數
recursive_mutex() = default;
recursive_mutex(const recursive_mutex&) = delete;
//析構函數
~recursive_mutex() = default;
//加鎖
void lock();
//解鎖
void unlock();
//嘗試鎖
bool try_lock();
timed_mutex
定時鎖:
//構造函數
timed_mutex() = default;
timed_mutex(const timed_mutex&) = delete;
//析構函數
~timed_mutex() = default;
//加鎖
void lock();
//解鎖
void unlock();
//嘗試鎖
bool try_lock();
//等待鎖,在調用時,在一個時間段內,如果鎖被釋放,加鎖,否則,返回false。
template <class _Rep, class _Period>
bool try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) { return _M_try_lock_for(__rtime); }
//等待鎖,在某個時刻到達之前,如果鎖被釋放,加鎖,否則,返回false
template <class _Clock, class _Duration>
bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) { return _M_try_lock_until(__atime); }
recursive_timed_mutex
遞歸定時鎖:具備遞歸鎖和定時鎖的所有特性。
//構造函數
recursive_timed_mutex() = default;
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
//析構函數
~recursive_timed_mutex() = default;
//加鎖
void lock();
//解鎖
void unlock();
//嘗試鎖
bool try_lock();
//等待鎖,在調用時,在一個時間段內,如果鎖被釋放,加鎖,否則,返回false。
template <class _Rep, class _Period>
bool try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) { return _M_try_lock_for(__rtime); }
//等待鎖,在某個時刻到達之前,如果鎖被釋放,加鎖,否則,返回false
template <class _Clock, class _Duration>
bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) { return _M_try_lock_until(__atime); }
lock_guard
//構造函數
explicit lock_guard(mutex_type& __m) : _M_device(__m) { _M_device.lock(); }
lock_guard(mutex_type& __m, adopt_lock_t) noexcept : _M_device(__m) { } // calling thread owns mutex
lock_guard(const lock_guard&) = delete;
//析構函數
~lock_guard() { _M_device.unlock(); }
個人理解:類似於智能指針,在生命週期結束時,析構,能夠自動解鎖,不需要手動解鎖,提供了一定的安全性。
unique_lock
//構造函數
unique_lock() noexcept : _M_device(0), _M_owns(false) { }
//構造函數,只允許顯示調用
explicit unique_lock(mutex_type& __m) : _M_device(std::__addressof(__m)), _M_owns(false) { lock(); _M_owns = true; }
//構造函數,無互斥所有權
unique_lock(mutex_type& __m, defer_lock_t) noexcept: _M_device(std::__addressof(__m)), _owns(false){ }
//構造函數,嘗試獲得鎖的所有權,而不阻塞
unique_lock(mutex_type& __m, try_to_lock_t) : _M_device(std::__addressof(__m)), M_owns(_M_device->try_lock()) { }
//構造函數,擁有互斥鎖所有權
unique_lock(mutex_type& __m, adopt_lock_t) noexcept : _M_device(std::__addressof(__m)), M_owns(true){
// XXX calling thread owns mutex
}
//析構函數
~unique_lock() { if (_M_owns) unlock();}
//鎖
void lock();
//嘗試鎖
bool try_lock();
//等待
bool try_lock_until();
bool try_lock_for();
//解鎖
void unlock();
//交換所有權
void swap(unique_lock& __u);
//釋放
mutex_type* release();
//返回鎖狀態
bool owns_lock();
//獲取鎖
mutex_type* mutex() const noexcept { return _M_device; }
個人理解:對鎖對象的控制權,百度來的,通用互斥包裝器,允許“延遲鎖定,鎖定的有限嘗試、遞歸鎖定、所有權轉移和條件變量一同使用”,unique_lock 比 lock_guard 使用更加靈活,功能更加強大。但是使用unique_lock 需要付出更多的時間成本、性能成本。
std::condition_variable
std::condition_variable 條件變量,性能消耗小於std::mutex,對於線程同步,效率高於 std::mutex。std::conditon_variable 有兩個接口 wait(),可以是線程處與休眠狀態,另一個就是notify_one(),喚醒處於wait中的其中一個條件變量,(可能當時有很多條件變量處於wait狀態)。notify_all(),喚醒所有處於wait狀態的條件。
//構造函數
condition_variable() noexcept;
condition_variable(const condition_variable&) = delete;
//析構函數
~condition_variable() noexcept;
//喚醒單個線程
void notify_one() noexcept;
//喚醒所有線程
void notify_all() noexcept;
//休眠函數
void wait(unique_lock<mutex>& __lock) noexcept;
void wait(unique_lock<mutex>& __lock, _Predicate __p) { while (!__p()) wait(__lock);}
//休眠函數,等待時間點,線程收到通知或者指定時間點abs_time超時之前,線程都會處於阻塞狀態,超時或者被喚醒,返回
cv_status wait_until(unique_lock<mutex>& __lock, const chrono::time_point<__clock_t, _Duration>& __atime) { return __wait_until_impl(__lock, __atime); }
cv_status wait_until(unique_lock<mutex>& __lock,const chrono::time_point<_Clock, _Duration>& __atime)
{
// DR 887 - Sync unknown clock to known clock.
const typename _Clock::time_point __c_entry = _Clock::now();
const __clock_t::time_point __s_entry = __clock_t::now();
const auto __delta = __atime - __c_entry;
const auto __s_atime = __s_entry + __delta;
return __wait_until_impl(__lock, __s_atime);
}
bool wait_until(unique_lock<mutex>& __lock,const chrono::time_point<_Clock, _Duration>& __atime,
_Predicate __p) { while (!__p()) if (wait_until(__lock, __atime) == cv_status::timeout) return __p();
return true;}
//線程休眠,加了時間限制,在超時之前,處於休眠狀態,如果超時或者被喚醒,wait_for 返回
cv_status wait_for(unique_lock<mutex>& __lock, const chrono::duration<_Rep, _Period>& __rtime)
{ return wait_until(__lock, __clock_t::now() + __rtime); }
bool wait_for(unique_lock<mutex>& __lock, const chrono::duration<_Rep, _Period>& __rtime,
_Predicate __p){ return wait_until(__lock, __clock_t::now() + __rtime, std::move(__p)); }