前言:這篇文章僅針對沒有使用過c++11線程庫的童鞋來快速入門,也是自己的一個簡單記錄,內容比較基礎。
-1.線程的基本使用
-2.互斥量
-3.條件變量
-4.原子變量
#include
#include
#include
#include
#include
int k = 0;
void fun(void)
{
//線程休眠,chrono是c++11的時間相關庫。
std::this_thread::sleep_for(std::chrono::seconds(3));
for(int i = 0; i < 10; ++i)
{
std::cout << hello world << std::endl;
k++;
}
}
int main(int argc, char *argv[])
{
//創建線程對象
std::thread t1(fun);
//輸出線程id和cpu核數
std::cout << ID: << t1.get_id() << std::endl;
std::cout << CPU: << std::thread::hardware_concurrency() << std::endl;
//主函數阻塞等待線程結束
t1.join();
//主函數和線程函數分離執行,線程變為後台線程
//t1.detach();
std::cout << k << std::endl;
return EXIT_SUCCESS;
}
注意:
1.linux下用gcc或clang必須加-pthread連接到線程庫,否則會出錯。
2.主線程函數不能提前結束於新創建的線程函數,因為在c++11中,線程也是對象,主函數結束線程對象即銷毀。
3.t.join()是主函數阻塞等待線程結束才能結束,主函數會繼續執行,並阻塞在return處
t.detach()主函數和線程函數分離,各自執行各自的,線程變為後台線程。
4.可通過bind和lambda創建線程
可以將線程保存在容器中,以保證線程對象的聲明周期。
但是注意線程沒有拷貝構造函數,有移動構造函數。
圖上可以看出拷貝構造函數為delete。
分為4種
std::mutex 獨占的互斥量,不能遞歸使用
std::timed_mutex 帶超時的獨占的互斥量,不能遞歸使用
std::recursive_mutex 遞歸互斥量,不帶超時功能
std::recursive_timed_mutex 帶超時的遞歸互斥量
#include
#include
#include
#include
#include
#include
std::mutex g_lock;
int i = 0;
void func(void)
{
//使用RAII手法,在離開作用域時自動釋放
std::lock_guardlocker(g_lock);
//正常的互斥鎖上鎖
//g_lock.lock();
i++;
std::cout << i << std::endl;
//互斥鎖解鎖
//g_lock.unlock();
}
int main(int argc, char *argv[])
{
std::thread t1(func);
std::thread t2(func);
std::thread t3(func);
t1.join();
t2.join();
t3.join();
return EXIT_SUCCESS;
}
注意:
1.多次獲取互斥量可能會發生死鎖,所以我們調用std::recursive_mutex遞歸鎖,允許同一線程多次獲得該鎖,一般不要使用遞歸鎖,原因:<1.用到遞歸鎖會使得程序的邏輯變復雜,使用到遞歸鎖的程序一般可以簡化。<2.遞歸鎖比非遞歸鎖效率低。<3.遞歸鎖的可重入次數是有限的,超過也會報錯。
2.可以使用帶超時時間的互斥鎖,避免阻塞在等待互斥鎖上。
3.unique_lock: 是一個通用的互斥量封裝類。與lock_guard不同,它還支持延遲加鎖、時間鎖、遞歸鎖、鎖所有權的轉移並且還支持使用條件變量。這也是一個不可復制的類,但它是可以移動的類。
阻塞一個或多個線程,直到收到另外一個線程發來的通知或者超時,才會喚醒當前阻塞的進程
條件變量需要和互斥量配合使用
c++11提供了兩種條件變量
1.std::condition_variable,配合std::unique_lock進行wait操作
2.std::condition_variable_any,和任意帶有lock,unlock的mutex進行搭配使用,比較靈活但效率略低。
條件變量的wait還有一個重載的方法,可以設置一個條件,條件變量會先檢查判斷式是否滿足條件。
原理:
當 std::condition_variable 對象的某個 wait 函數被調用的時候,它使用 std::unique_lock(通過 std::mutex) 來鎖住當前線程。當前線程會一直被阻塞,直到另外一個線程在相同的 std::condition_variable 對象上調用了 notification 函數來喚醒當前線程。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
template
class SynQueue
{
public:
//構造函數
SynQueue(int MaxSize):
m_maxsize(MaxSize) { }
//將T類型對象放入隊列
void Put(const T&x)
{
std::lock_guardlocker(m_mutex);
while(isFull())
{
//如果滿了,等待
m_notFull.wait(m_mutex);
}
m_queue.push_back(x);
//通過條件變量喚醒一個線程,也可以所有線程
m_notEmpty.notify_one();
}
//將T類型對象從隊列取出
void Take(T&x)
{
std::lock_guard locker(m_mutex);
while(isEmpty())
{
std::cout << no resource... please wait << std::endl;
m_notEmpty.wait(m_mutex);
}
x = m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();
}
//判斷隊列是否為空
bool Empty()
{
std::lock_guard locker(m_mutex);
return m_queue.empty();
}
//判斷隊列是否為滿
bool Full()
{
std::lock_guard locker(m_mutex);
return m_queue.size() == m_maxsize;
}
//返回隊列大小
size_t Size()
{
std::lock_guard locker(m_mutex);
return m_queue.size();
}
private:
//判斷空或滿,內部使用不需要加鎖
bool isFull() const
{
return m_queue.size() == m_maxsize;
}
bool isEmpty() const
{
return m_queue.empty();
}
private:
//隊列
std::listm_queue;
//互斥鎖
std::mutex m_mutex;
//不為空時的條件變量
std::condition_variable_any m_notEmpty;
//不為滿時的條件變量
std::condition_variable_any m_notFull;
//隊列最大長度
int m_maxsize;
};
void func(SynQueue *sq)
{
int ret;
sq->Take(ret);
std::cout << ret << std::endl;
}
int main(int argc, char *argv[])
{
//創建線程隊列,長度最大為20
SynQueuesyn(20);
//放置數據對象
for(int i = 0; i < 10; i++)
{
syn.Put(i);
}
std::cout << syn.Size() << std::endl;
//線程不能拷貝,用容器和智能指針來管理線程生存
std::vector> tvec;
//多循環一次,資源不足,阻塞最後一個線程,在後面添加一個資源,看該線程是否會被喚醒執行。
for(int i = 0; i < 11; i++)
{
//創建線程並且將管理線程的智能指針保存到容器中
tvec.push_back(std::make_shared(func, &syn));
//變為後台線程
tvec[i]->detach();
}
sleep(10);
//添加一個資源
syn.Put(11);
sleep(10);
return EXIT_SUCCESS;
}
運行結果:
原子變量,為原子操作,不需要加鎖
std::atomic
詳情可參考,這裡僅簡單舉例用法
cppreference atomic
#include
#include
#include
#include
#include
#include
#include
//創建int類型的原子變量
std::atomicatc(0);
void func()
{
std::cout << atc << std::endl;
原子變量自增
atc++;
}
int main(int argc, char *argv[])
{
std::vectortvec;
for(int i = 0; i < 10; i++)
{
std::thread t(func);
//線程對象移動語義
tvec.push_back(std::move(t));
tvec[i].join();
}
return EXIT_SUCCESS;
}