天天看點

C++:線程(std::thread)1.建立一個線程2.thread::join()3.thread::detach()4.mutex5.std::lock_guard

1.建立一個線程

建立線程比較簡單,使用std的thread執行個體化一個線程對象就建立完成了,示例:

#include <iostream>
#include <thread>
using namespace std;

void t1()  //普通的函數,用來執行線程
{
    for (int i = 0; i < 20; ++i)
    {
        cout << "t1111\n";
    }
}
void t2()
{
    for (int i = 0; i < 20; ++i)
    {
        cout << "t22222\n";
    }
}
int main()
{
    thread th1(t1);  //執行個體化一個線程對象th1,使用函數t1構造,然後該線程就開始執行了(t1())
    thread th2(t2);

    cout << "here is main\n\n";

    return 0;
}
           

不過這個示例是有問題的,因為在建立了線程後線程開始執行,但是主線程main()并沒有停止腳步,仍然繼續執行然後退出,此時線程對象還是joinable的,線程仍然存在但指向它的線程對象已經銷毀,是以會抛出異常。

C++:線程(std::thread)1.建立一個線程2.thread::join()3.thread::detach()4.mutex5.std::lock_guard

那麼該如何保證子線程執行完了退出後再退出主線程呢?

2.thread::join()

使用join接口可以解決上述問題,join的作用是讓主線程等待直到該子線程執行結束,示例:

 #include <iostream>
#include <thread>
using namespace std;

void t1()
{
    for (int i = 0; i < 20; ++i)
    {
        cout << "t1111\n";
    }
}
void t2()
{
    for (int i = 0; i < 20; ++i)
    {
        cout << "t22222\n";
    }
}
int main()
{
    thread th1(t1);
    thread th2(t2);
    
    th1.join(); //等待th1執行完
    th2.join(); //等待th2執行完

    cout << "here is main\n\n";

    return 0;
}
           

此時就可以正常地執行子線程了,同時注意最後一個輸出,說明了main是等待子線程結束才繼續執行的

C++:線程(std::thread)1.建立一個線程2.thread::join()3.thread::detach()4.mutex5.std::lock_guard

 需要注意的是線程對象執行了join後就不再joinable了,是以隻能調用join一次。

3.thread::detach()

(1.)中提到的問題,還可以使用detach來解決,detach是用來和線程對象分離的,這樣線程可以獨立地執行,不過這樣由于沒有thread對象指向該線程而失去了對它的控制,當對象析構時線程會繼續在背景執行,但是當主程式退出時并不能保證線程能執行完。如果沒有良好的控制機制或者這種背景線程比較重要,最好不用detach而應該使用join。

  int main()
{
    thread th1(t1);
    thread th2(t2);
    
    th1.detach();
    th2.detach();

    cout << "here is main\n\n";

    return 0;
}
           
C++:線程(std::thread)1.建立一個線程2.thread::join()3.thread::detach()4.mutex5.std::lock_guard

由結果可見線程并沒有執行完而退出:

4.mutex

頭檔案是,mutex是用來保證線程同步的,防止不同的線程同時操作同一個共享資料。

 int cnt = 20;
mutex m;
void t1()
{
    while (cnt > 0)
    {    
        m.lock();
        
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }

        m.unlock();
    }
}
void t2()
{
    while (cnt > 0)
    {
        m.lock();
        
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }

        m.unlock();
    }
}
int main()
{
    
    thread th1(t1);
    thread th2(t2);
    
    th1.join();
    th2.join();

    return 0;
}
           

運作結果,cnt是依次遞減的,沒有因為多線程而打亂次序:

C++:線程(std::thread)1.建立一個線程2.thread::join()3.thread::detach()4.mutex5.std::lock_guard

但是使用mutex是不安全的,當一個線程在解鎖之前異常退出了,那麼其它被阻塞的線程就無法繼續下去。

5.std::lock_guard

使用lock_guard則相對安全,它是基于作用域的,能夠自解鎖,當該對象建立時,它會像m.lock()一樣獲得互斥鎖,當生命周期結束時,它會自動析構(unlock),不會因為某個線程異常退出而影響其他線程。示例:

 int cnt = 20;
mutex m;
void t1()
{
    while (cnt > 0)
    {    
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }
        
    }
}
void t2()
{
    while (cnt > 0)
    {
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0)
        {
            --cnt;
            cout << cnt << endl;
        }
    
    }
}
           

繼續閱讀