c++多執行緒

拾墨、發表於2024-03-08

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;
}

相關文章