1. 程式人生 > 其它 >C++11多執行緒基礎

C++11多執行緒基礎

1. join

#include <iostream>
#include <thread>

using namespace std;

void fun()   //子執行緒
{
    for (int i = 0; i < 100; i++)
        cout << "*";
}

void main()
{
    thread t(fun);   //建立執行緒,執行緒從fun()函式開始執行
    t.join();   //阻塞主執行緒,讓主執行緒等待子執行緒執行完畢

    for (int i = 0; i < 100; i++)   //主執行緒
cout << "$"; }

輸出:****************************************************************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

#include <iostream>
#include <thread>

using namespace
std; void fun() //子執行緒 { for (int i = 0; i < 100; i++) cout << "*"; } void main() { thread t(fun); //建立執行緒,執行緒從fun()函式開始執行 for (int i = 0; i < 100; i++) //主執行緒 cout << "$"; t.join(); //阻塞主執行緒,讓主執行緒等待子執行緒執行完畢 }

輸出:

*****************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*******************$$$$$$$$$*************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$***

#include <iostream>
#include <thread>

using namespace std;

void fun()   //子執行緒
{
    for (int i = 0; i < 100; i++)
        cout << "*";
}

void main()
{
    thread t(fun);   //建立執行緒,執行緒從fun()函式開始執行

    for (int i = 0; i < 100; i++)   //主執行緒
        cout << "$";
}

輸出:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16$$ 17$$ 18 19 20 21 22 23 24 25 26 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$27

2. detach

#include <iostream>
#include <thread>

using namespace std;

void fun()   //子執行緒
{
    for (int i = 0; i < 100; i++)
        cout << i << " ";
}

void main()
{
    thread t(fun);   //建立執行緒,執行緒從fun()函式開始執行
    t.detach();   //主執行緒不等待子執行緒執行完畢就可以先行退出,子執行緒駐留在後臺執行

    for (int i = 0; i < 100; i++)   //主執行緒
        cout << "$";
}

輸出:

0 1 2 3 4 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$5

3. joinable

#include <iostream>
#include <thread>

using namespace std;

void fun()   //子執行緒
{
    for (int i = 0; i < 100; i++)
        cout << i << " ";
}

void main()
{
    thread t(fun);   //建立執行緒,執行緒從fun()函式開始執行

    if (t.joinable())   //判斷是否可以成功使用join()或者detach(),呼叫join後不能再呼叫join和detach,呼叫detach後也不能再呼叫join和detach。因為重複呼叫會導致異常
    {
        t.join();
        cout << "joinable() == true" << endl;
    }

    for (int i = 0; i < 100; i++)   //主執行緒
        cout << "$";
}

輸出:****************************************************************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

4. 可呼叫物件

#include <iostream>
#include <thread>

using namespace std;class A
{
public:
    void operator()()   //不能帶引數
    {
        for (int i = 0; i < 100; i++)   //子執行緒
            cout << i << " ";
    }
};

void main()
{
    A a;   //a:可呼叫物件
    thread t(a);   //將物件a複製到子執行緒中,執行完主執行緒後,物件a會被銷燬,但是複製到子執行緒中的物件依舊存在。只要這個A類物件沒有主執行緒中的引用或指標,就不會出現問題。
    t.join();   //阻塞主執行緒,讓主執行緒等待子執行緒執行完畢

    for (int i = 0; i < 100; i++)   //主執行緒
        cout << "$";
}

輸出:****************************************************************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

5.lambda表示式

#include <iostream>
#include <thread>

using namespace std;void main()
{
    auto lamthread = [] {

        for (int i = 0; i < 100; i++)
            cout << i << " ";
    };

    thread t(lamthread);
    t.join();
}

輸出:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

6. 陷阱

#include <iostream>
#include <thread>

using namespace std;

void fun(const int& i, char* buf)   //子執行緒
{
    cout << i << endl;
    cout << buf << endl;
}

void main()
{
    int num = 9;
    char buf[] = "abc";

    thread t(fun, num, buf);
    t.join();

    cout << "i love you" << endl;
}

輸出:

9
abc
i love you

#include <iostream>
#include <thread>

using namespace std;

void fun(const int& i, char* buf)   //子執行緒
{
    cout << i << endl;   //i並不是num的引用,實際是值傳遞。即使主執行緒使用detach,子執行緒中的i也是安全的。最好不使用引用。
    cout << buf << endl;   //指標在主執行緒使用detach時,絕對會有問題。因為主執行緒中的buf已經被釋放,所以子執行緒中的buf就變成了野指標。
}

void main()
{
    int num = 9;
    char buf[] = "abc";

    thread t(fun, num, buf);   //有問題
    t.detach();
}
#include <iostream>
#include <thread>

using namespace std;

void fun(const int& i, string& buf)   //子執行緒
{
    cout << i << endl;
    cout << buf.c_str() << endl;
}

void main()
{
    int num = 9;
    char buf[] = "abc";

    thread t(fun, num, string(buf));   //沒問題。用臨時構造的string類物件作為引數傳遞給執行緒,一定能在主執行緒執行完畢前把執行緒函式的第二個引數構造出來,從而確保主執行緒即便detach了子執行緒也能安全執行。
    t.detach();
}
#include <iostream>
#include <thread>

using namespace std;

class A
{
public:
    //型別轉換建構函式,可以把一個int轉換成一個類A物件
    A(int i) :m_i(i) { cout << "呼叫建構函式" << this << endl; }
    A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << endl; }
    A() { cout << "呼叫解構函式" << this << endl; }
private:
    int m_i;
};

void fun(const int& i, const A& a)   //子執行緒
{
    cout << i << endl;
    cout << &a << endl;   //輸出物件地址
}

void main()
{
    int num = 9;int sum = 0;

    thread t(fun, num, sum);   //有時候都還沒呼叫建構函式主執行緒就退出了,則會有問題。需要改成建立臨時物件thread t(fun, num, A(sum));
    t.detach();
}
#include <iostream>
#include <thread>

using namespace std;

class A
{
public:
    //型別轉換建構函式,可以把一個int轉換成一個類A物件
    A(int i) :m_i(i) { cout << "呼叫建構函式" << this << endl; }
    A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << endl; }
    A() { cout << "呼叫解構函式" << this << endl; }
private:
    int m_i;
};

void fun(const int& i, const A& a)   //子執行緒
{
    cout << i << endl;
    cout << &a << endl;   //輸出物件地址
}

void main()
{
    int num = 9;int sum = 0;

    thread t(fun, num, A(sum));   //絕對沒問題。絕對在先呼叫建構函式後再退出主執行緒
    t.detach();
}

輸出:

呼叫建構函式00ADF884
呼叫拷貝建構函式}00E54B58
9

8. 執行緒id

驗證:

#include <iostream>
#include <thread>

using namespace std;

class A
{
public:
    //型別轉換建構函式,可以把一個int轉換成一個類A物件
    A(int i) :m_i(i) { cout << "呼叫建構函式" << this << " threadid:" << std::this_thread::get_id() << endl; }
    A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << " threadid:" << std::this_thread::get_id() << endl; }
    A() { cout << "呼叫解構函式" << this << " threadid:" << std::this_thread::get_id() << endl; }
private:
    int m_i;
};

void fun(const A& a)   //子執行緒
{
    cout << "a threadid:" << std::this_thread::get_id() << endl;
    cout << &a << endl;   //輸出物件地址
}

void main()
{
    cout << "主執行緒id:" << std::this_thread::get_id() << endl;

    int num = 9;int sum = 0;

    thread t(fun, sum);   //在子執行緒中構造的A類物件。當呼叫detach時顯然會出問題,因為sum已釋放,則子執行緒中的sum就是不可預知的值。
    t.join();
}

輸出:

主執行緒id:5300
呼叫建構函式00CCF7E4 threadid:6380
a threadid:6380
00CCF7E4

#include <iostream>
#include <thread>

using namespace std;

class A
{
public:
    //型別轉換建構函式,可以把一個int轉換成一個類A物件
    A(int i) :m_i(i) { cout << "呼叫建構函式" << this << " threadid:" << std::this_thread::get_id() << endl; }
    A(const A& a) :m_i(a.m_i) { cout << "呼叫拷貝建構函式}" << this << " threadid:" << std::this_thread::get_id() << endl; }
    A() { cout << "呼叫解構函式" << this << " threadid:" << std::this_thread::get_id() << endl; }
private:
    int m_i;
};

void fun(const A& a)   //子執行緒。  用引用傳遞,否則得多呼叫一次拷貝建構函式
{
    cout << "a threadid:" << std::this_thread::get_id() << endl;
    cout << &a << endl;   //輸出物件地址
}

void main()
{
    cout << "主執行緒id:" << std::this_thread::get_id() << endl;

    int num = 9;
    int sum = 0;

    thread t(fun, A(sum));   //主執行緒中構造的A類物件。當呼叫detach時也不會有問題
    t.join();
}

輸出:

主執行緒id:18328
呼叫建構函式003CF874 threadid:18328
呼叫拷貝建構函式}0043AD88 threadid:18328
a threadid:14056
0043AD88

8.