1. 執行緒庫的使用
建立程序
# include<iostream>
# include<thread> //執行緒庫標頭檔案
using namespace std;
void thread_1()
{
cout<<"子執行緒1"<<endl;
}
int thread_2(int x)
{
cout<<"子執行緒2"<<endl;
return x;
}
int main()
{
//thread函式建立一個新執行緒
//first是執行緒名,括號裡第一個引數是函式名,第二個引數是給第一個函式的引數
thread first(thread_1);
thread second(thread_2 , 100);
//若沒有下面這兩行,程式會報錯
//因為若沒有下面這兩行程式會一直執行下去。
//很有可能在主執行緒return 0後,子執行緒還沒有繼續進行
first.detach();
second.join();
return 0;
}
join和detach
first.join(); //主程序會等待子程序first執行完畢後才繼續執行
second.detach(); //主程序不會等待子程序,子執行緒在後臺執行
joinable
判斷執行緒是否可以join,返回bool型別
若對一個不能使用join或detach的執行緒強行使用join或detach會報一個system_error
所以在一個比較嚴謹的專案中會先判斷一下是否可以使用
int main()
{
thread first(thread_1);
thread second(thread_2 , 100);
first.detach();
//second.detach();
if(second.joinable())
{
second.join();
}
cout<<"主程序";
return 0;
}
2. 互斥鎖mutex(互斥量)
如下面的程式碼
當兩個執行緒都對一個全域性變數進行操作時候。若a = 3,兩個執行緒恰好在同一時刻執行a++,那麼a會等於4而不是我們下意識想到的5
為了避免這個情況,我們使用互斥鎖mutex
當執行緒遇到lock()
若沒上鎖,則上鎖,其他執行緒無法訪問
若已上鎖,則等待解鎖
# include<iostream>
# include<thread>
# include<mutex>
using namespace std;
int a = 1;
mutex mtx;
void f()
{
for(int i=1;i<=1000;i++)
{
mtx.lock(); //上鎖
a++;
mtx.unlock(); //解鎖
}
}
int main()
{
thread t1(f);
thread t2(f);
t1.join();t2.join();
cout<<a;
return 0;
}
3. 原子操作
原子操作就是: 不可中斷的一個或者一系列操作, 也就是不會被執行緒排程機制打斷的操作, 執行期間不會有任何的上下文切換(context switch).
講第二個例子中的a改成atomic
atomic<int> a = 1
void f()
{
for(int i=1;i<=1000;i++)
{
a++;
}
}
int main()
{
thread t1(f);
thread t2(f);
t1.join();t2.join();
cout<<a;
return 0;
}
4. 互斥量死鎖
下面的兩個執行緒可能會發生死鎖:
f1佔有m1資源,f2佔有m2資源,然後兩個都在等待對方釋放資源
mutex m1;
mutex m2;
void f1()
{
for(int i=1;i<=1000;i++)
{
m1.lock();
m2.lock();
m1.unlock();
m2.unlock();
}
}
void f2()
{
for(int i=1;i<=1000;i++)
{
m2.lock();
m1.lock();
m1.unlock();
m2.unlock();
}
}
只需要將f2改成下面這樣就好了
void f2()
{
for(int i=1;i<=1000;i++)
{
m1.lock();
m2.lock();
m1.unlock();
m2.unlock();
}
}
5. unique lock
實現自動解鎖
int a = 1;
mutex mtx;
void f()
{
for(int i=1;i<=1000;i++)
{
unique_lock<mutex>lg(mtx);
a++;
}
}
int main()
{
thread t1(f);
thread t2(f);
t1.join();t2.join();
cout<<a;
return 0;
}