thread
thread頭文件主要包含thread類和this_thread命名空間。
namespace std {
namespace this_thread {
thread::id get_id() noexcept;
void yield() noexcept; //建議實現重新調度各執行線程,以允許其他線程的運行
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& rel_time);
}
}
thread::id是一個輕量級的平凡拷貝的類,作爲std::thread對象的唯一標識符,庫爲它實現了一些比較操作符、operator <<、和hash特化。
namespace std {
class thread {
public:
class id;
typedef /* 由實現定義 */ native_handle_type;
// 構造/複製/銷燬:
thread() noexcept;
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
~thread();
thread(const thread&) = delete;
thread(thread&&) noexcept;
thread& operator=(const thread&) = delete;
thread& operator=(thread&&) noexcept;
// 成員:
void swap(thread&) noexcept;
bool joinable() const noexcept; //檢查 thread 對象是否標識活躍的執行線程
void join(); //阻塞當前線程,直至 *this 所標識的線程完成其執行
void detach();
id get_id() const noexcept;
native_handle_type native_handle();
// 靜態成員:
static unsigned hardware_concurrency() noexcept;
};
}
如果一個thread對象標識着一個活躍的運行線程,稱之爲joinable,具體來說是判斷std::thread::get_id()的返回值是不是等於std::thread::id()(std::thread::id的默認值)。因此thread經默認構造後不是joinable的;一個運行完畢但還沒被joined的線程仍被認爲是活躍的,所以是joinable的;一個被joined的線程是非joinable的。
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t;
std::cout << "before starting, joinable: " << std::boolalpha << t.joinable() << '\n';
t = std::thread(foo);
std::cout << "after starting, joinable: " << t.joinable() << '\n';
t.join();
std::cout << "after joining, joinable: " << t.joinable()<< '\n';
}
mutex
mutex頭文件主要包括:
- mutex系列類
std::mutex,最基本的 mutex 類。
std::timed_mutex,定時 mutex 類。
std::recursive_mutex,遞歸 mutex 類。
std::recursive_timed_mutex,定時遞歸 mutex 類。 - lock 類
std::lock_guard,方便線程對互斥量上鎖。
std::unique_lock,方便線程對互斥量上鎖,但提供了更詳細的上鎖和解鎖控制。 - 輔助類型
std::once_flag
std::adopt_lock_t
std::defer_lock_t
std::try_to_lock_t - 函數
std::try_lock,嘗試同時對多個互斥量上鎖。
std::lock,可以同時對多個互斥量上鎖。
std::call_once,如果多個線程需要同時調用某個函數,call_once 可以保證多個線程對該函數只調用一次。
mutex的概要聲明:
class mutex {
public:
constexpr mutex() noexcept;
~mutex();
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
void lock();
bool try_lock();
void unlock();
typedef /* 由實現定義 */ native_handle_type;
native_handle_type native_handle();
};
recursive_mutex 允許同一個線程對互斥量多次上鎖,來獲得對互斥量對象的多層所有權。
timed_mutex 比 mutex 多了兩個成員函數:
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
try_lock_for 接受一個時間範圍,表示在這一段時間範圍之內線程如果沒有獲得鎖則被阻塞住,如果在此期間獲得了對互斥量的鎖,返回true,如果超時仍未獲得鎖,則返回 false。
m.try_lock_until(std::chrono::system_clock::now() + std::chrono::seconds(5));
等價於m.try_lock_for(std::chrono::seconds(5));
lock_guard和unique_lock
lock_guard是一個mutex wrapper,概要聲明:
template <class Mutex>
class lock_guard {
public:
typedef Mutex mutex_type;
explicit lock_guard(mutex_type& m);
lock_guard(mutex_type& m, adopt_lock_t);
~lock_guard();
lock_guard(lock_guard const&) = delete;
lock_guard& operator=(lock_guard const&) = delete;
private:
mutex_type& pm;
};
構造時加鎖,析構時解鎖,adopt構造接管一個已上鎖的mutex。
unique_lock實現的功能類似,但提供更精細的控制,概要聲明:
template <class Mutex>
class unique_lock {
public:
typedef Mutex mutex_type;
// 構造/複製/銷燬:
unique_lock() noexcept; (1)
unique_lock( unique_lock&& other ) noexcept; (2)
explicit unique_lock(mutex_type& m); (3)
unique_lock(mutex_type& m, defer_lock_t) noexcept; (4)
unique_lock(mutex_type& m, try_to_lock_t); (5)
unique_lock(mutex_type& m, adopt_lock_t); (6)
template <class Clock, class Duration>
unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time); (7)
template <class Rep, class Period>
unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time); (8)
~unique_lock();
unique_lock(unique_lock const&) = delete;
unique_lock& operator=(unique_lock const&) = delete;
unique_lock(unique_lock&& u) noexcept;
unique_lock& operator=(unique_lock&& u) noexcept;
// 鎖定:
void lock();
bool try_lock();
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
void unlock();
// 修改器:
void swap(unique_lock& u) noexcept;
mutex_type *release() noexcept; (9)
// 觀察器:
bool owns_lock() const noexcept; //檢查mutex是否已上鎖
explicit operator bool () const noexcept; //同上
mutex_type* mutex() const noexcept;
private:
mutex_type *pm;
bool owns;
};
(1) 構造無關聯互斥的 unique_lock。
(2) 移動構造函數。以 other 的內容初始化 unique_lock 。令 other 無關聯互斥。
(3-8) 構造以 m 爲關聯互斥的 unique_lock 。另外:
3) 通過調用 m.lock() 鎖定關聯互斥。
4) 不鎖定關聯互斥。
5) 通過調用 m.try_lock() 嘗試鎖定關聯互斥而不阻塞。
6) 假定調用方線程已佔有 m 。
7) 通過調用 m.try_lock_for(timeout_duration) 嘗試鎖定關聯互斥。
8) 通過調用 m.try_lock_until(timeout_time) 嘗試鎖定關聯互斥。
(9) release斷開*this
和對應mutex(如果存在的話)的聯繫。release不會進行解鎖操作,因此如果*this
擁有mutex的所有權,調用者應該負責mutex的解鎖。release返回對應mutex,如果沒有對應mutex則返回空指針。
std::adopt_lock_t,std::defer_lock_t,std::try_to_lock_t是爲了輔助lock_guard和unique_lock的結構體類型,分別有std::adopt_lock,std::defer_lock,std::try_to_lock常量對象存在。
函數
// 依次調用每個對象的 try_lock
// 若調用 try_lock 失敗,則不再進一步調用 try_lock,並對任何已鎖對象調用 unlock
// 若調用 try_lock 拋出異常,則在重拋前對任何已鎖對象調用 unlock
// 成功時返回 -1 ,否則爲鎖定失敗對象的下標值(從0開始計數)
template <class L1, class L2, class... L3>
int try_lock(L1&, L2&, L3&...);
// 鎖定給定的可鎖定對象,使用免死鎖算法避免死鎖
// 若調用 lock 或 unlock 導致異常,則在重拋前對任何已鎖的對象調用 unlock
template <class L1, class L2, class... L3>
void lock(L1&, L2&, L3&...);
struct once_flag {
constexpr once_flag() noexcept;
once_flag(const once_flag&) = delete;
once_flag& operator=(const once_flag&) = delete;
};
template<class Callable, class ...Args>
void call_once(once_flag& flag, Callable func, Args&&... args);
call_once 執行一次可調用對象 f ,即使同時從多個線程調用。細節爲:
- 若在調用 call_once 的時刻, flag 指示已經調用了 f ,則 call_once 立即返回。
- 否則, call_once 以參數
std::forward<Args>(args)...
調用 std::forward<Callable>(f)
。不同於 std::thread 構造函數或 std::async ,call_once 不移動或複製參數,因爲不需要轉移它們到另一執行線程。- 若該調用拋異常,則傳播異常給 call_once 的調用方,並且不翻轉 flag ,以令其他調用繼續嘗試。
- 若該調用正常返回,則翻轉 flag ,並保證以同一 flag 對 call_once 的其他調用立即返回。
call_once 的例子:
#include <iostream>
#include <thread>
#include <mutex>
std::once_flag flag1, flag2;
void simple_do_once()
{
std::call_once(flag1, [](){ std::cout << "Simple example: called once\n"; });
}
void may_throw_function(bool do_throw)
{
if (do_throw) {
std::cout << "throw: call_once will retry\n";
throw std::exception();
}
std::cout << "Didn't throw, call_once will not attempt again\n"; // 保證一次
}
void do_once(bool do_throw)
{
try {
std::call_once(flag2, may_throw_function, do_throw);
}
catch (...) {
}
}
int main()
{
std::thread st1(simple_do_once);
std::thread st2(simple_do_once);
std::thread st3(simple_do_once);
std::thread st4(simple_do_once);
st1.join();
st2.join();
st3.join();
st4.join();
std::thread t1(do_once, true);
std::thread t2(do_once, true);
std::thread t3(do_once, false);
std::thread t4(do_once, true);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
主要參考自cppreference