c++11併發語法初步

c++11併發語法初步

參考:ios

http://www.javashuo.com/article/p-fofmzcap-hk.htmlc++

https://en.cppreference.com/w/git

https://wizardforcel.gitbooks.io/cpp-11-faq/content/77.html編程

http://www.cplusplus.com/reference/promise

C++11多線程頭文件

C++11 新標準中引入了四個頭文件來支持多線程編程,他們分別是 , , , 多線程

  • :該頭文主要聲明瞭兩個類, std::atomic 和 std::atomic_flag,另外還聲明瞭一套 C 風格的原子類型和與 C 兼容的原子操做的函數。
  • :該頭文件主要聲明瞭 std::thread 類,另外 std::this_thread 命名空間也在該頭文件中。
  • :該頭文件主要聲明瞭與互斥量(mutex)相關的類,包括 std::mutex 系列類,std::lock_guard, std::unique_lock, 以及其餘的類型和函數。
  • :該頭文件主要聲明瞭與條件變量相關的類,包括 std::condition_variable 和 std::condition_variable_any。
  • :該頭文件主要聲明瞭 std::promise, std::package_task 兩個 Provider 類,以及 std::future 和 std::shared_future 兩個 Future 類,另外還有一些與之相關的類型和函數,std::async() 函數就聲明在此頭文件中。

多線程的Hello World

#include <bits/stdc++.h>
#include <thread>

using namespace std;

void function_name() {
    cout << "hello world" << endl;
}

int main()
{
    std::thread t(function_name);
    t.join();

    return 0;
}

std::thread 構造併發

  • (1). 默認構造函數,建立一個空的 thread 執行對象。
  • (2). 初始化構造函數,建立一個 thread對象,該 thread對象可被 joinable,新產生的線程會調用 fn 函數,該函數的參數由 args 給出。
  • (3). 拷貝構造函數(被禁用),意味着 thread 不可被拷貝構造。
  • (4). move 構造函數,move 構造函數,調用成功以後 x 不表明任何 thread 執行對象。

注意:可被 joinable 的 thread 對象必須在他們銷燬以前被主線程 join 或者將其設置爲 detached.異步

其餘函數async

get_id 獲取線程ID
joinable 檢查線程是否能夠join
join join線程
detach detach線程
swap swap線程
native_handle 返回native_handle
hardware_concurrency[static] 檢查硬件併發性

參考:https://en.cppreference.com/w/cpp/thread/thread/thread

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
 
void f1(int n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 1 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
void f2(int& n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 2 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
class foo
{
public:
    void bar()
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 3 executing\n";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    int n = 0;
};
 
int main()
{
    int n = 0;
    foo f;
    std::thread t1; // t1 is not a thread
    std::thread t2(f1, n + 1); // pass by value
    std::thread t3(f2, std::ref(n)); // pass by reference
    std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
    std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
    t2.join();
    t4.join();
    t5.join();
    std::cout << "Final value of n is " << n << '\n';
    std::cout << "Final value of foo::n is " << f.n << '\n';
}
Thread 1 executing
Thread 2 executing
Thread 3 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Thread 2 executing
Thread 3 executing
Thread 1 executing
Thread 3 executing
Thread 2 executing
Thread 1 executing
Thread 3 executing
Thread 1 executing
Thread 2 executing
Final value of n is 5
Final value of foo::n is 5
std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f

保留疑惑,函數名便是地址,加&仍是地址,可是看別人怎麼寫。

move (1) thread& operator= (thread&& rhs) noexcept;
copy [deleted] (2) thread& operator= (const thread&) = delete;
  • (1). move 賦值操做,若是當前對象不可 joinable,須要傳遞一個右值引用(rhs)給 move 賦值操做;若是當前對象可被 joinable,則 terminate() 報錯。
  • (2). 拷貝賦值操做被禁用,thread 對象不可被拷貝。

互斥量

Mutex 系列類(四種)

  • std::mutex,最基本的 Mutex 類。
  • std::recursive_mutex,遞歸 Mutex 類。
  • std::time_mutex,定時 Mutex 類。
  • std::recursive_timed_mutex,定時遞歸 Mutex 類。

Lock 類(兩種)

  • std::lock_guard,與 Mutex RAII 相關,方便線程對互斥量上鎖。
  • std::unique_lock,與 Mutex RAII 相關,方便線程對互斥量上鎖,但提供了更好的上鎖和解鎖控制。

其餘類型

  • std::once_flag
  • std::adopt_lock_t
  • std::defer_lock_t
  • std::try_to_lock_t

函數

  • std::try_lock,嘗試同時對多個互斥量上鎖。
  • std::lock,能夠同時對多個互斥量上鎖。
  • std::call_once,若是多個線程須要同時調用某個函數,call_once 能夠保證多個線程對該函數只調用一次。

併發最大的問題就是對臨界區資源的訪問,主要是經過互斥量上鎖的方法解決。

http://www.cplusplus.com/reference/mutex/mutex/try_lock/ 具體細節看庫便可,下面解析一些常見的

主要用unique_lock,在構造函數上鎖,在析構函數解鎖,第一個參數爲互斥量,第二個是可選

  • std::adopt_lock_t
  • std::defer_lock_t
  • std::try_to_lock_t

https://en.cppreference.com/w/cpp/thread/lock_tag

#include <bits/stdc++.h>

using namespace std;

int main()
{

    std::mutex my_mutex;
    std::unique_lock<std::mutex> lock(my_mutex);

    ///表示這個互斥量已經lock成功了,通知構造函數不用在lock了,
    my_mutex.lock();    ///必須先lock才能用
    std::unique_lock<std::mutex> lock1(my_mutex, std::adopt_lock);

    ///後面會本身unlock()


    ///============================
    ///嘗試鎖,沒鎖成功不阻塞, 前提互斥量不能被鎖定
    std::unique_lock<std::mutex> lock2(my_mutex, std::try_to_lock);
    if(lock.owns_lock()) {
        ///拿到了鎖頭

    } else {
        ///沒有拿到鎖頭
    }

    ///============================
    ///我沒加鎖,後面手動鎖,方便調用函數, 前提互斥量不能被鎖定
    std::unique_lock<std::mutex> lock3(my_mutex, std::defer_lock);
    lock3.lock();   ///加鎖

    lock3.unlock(); ///不加鎖

    if(lock3.try_lock() == true) {
        ///拿到鎖頭了
    } else {
        ///沒拿到

    }

    ///釋放互斥量
    std::mutex *m = lock3.release();

    return 0;
}

std::condition_variable

一個線程等待另外一個線程條件知足

void function_name() {
    unique_lock<mutex> lock(mutex1);

    ///能往下走,確定互斥量默認被鎖了,到wait。
    ///第二個參數可選,沒寫默認false。true爲不堵塞,繼續走。
    ///若是是false卡在這等待其餘線程的con.notify_one()的函數
    ///注意notify_one的時候必須卡在wait的地方,不然喚醒失敗
    ///notify_all()
    
    con.wait(lock, [] () {
        if(true) {
            return true;
        }
        return false;
    });
}

std::async, std::future 線程返回值

std::async啓動一個異步任務後,返回一個std::future訪問異步操做結果的機制

#include <bits/stdc++.h>
#include <future>

using namespace std;

int mythread() {
    cout << "mythread = " << this_thread::get_id() << endl;
    return 5;
}

int main () {

    cout << "main = " << this_thread::get_id() << endl;
    ///壓根沒建立子線程,在主線程中作,視乎默認是這個
    std::future<int>ret = std::async(std::launch::deferred, mythread);
  ///async 新線程在這裏開始
    std::future<int>ret = std::async(std::launch::async, mythread);
    cout << ret.get();  ///卡在這裏等結果
    return 0;
}

用packaged_task打包線程,就多了一個能夠返回結果的future接口

#include <bits/stdc++.h>
#include <future>
#include <thread>
using namespace std;
int mythread() {
    cout << "mythread = " << this_thread::get_id() << endl;
    return 5;
}
int main () {
    ///std::packaged_task 打包可調用對象
    std::packaged_task<int()>mypt(mythread);
    std::thread t1(std::ref(mypt));
    t1.join();

    std::future<int>res = mypt.get_future();
    return 0;
}

後續就能夠這麼用

///
    vector<std::std::packaged_task<int()>> vec;
    ///不用移動語義可能會出問題
    vec.push_back(std::move(mypt));
相關文章
相關標籤/搜索