国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

《C++并發(fā)編程實戰(zhàn)》讀書筆記(3):并發(fā)操作的同步

這篇具有很好參考價值的文章主要介紹了《C++并發(fā)編程實戰(zhàn)》讀書筆記(3):并發(fā)操作的同步。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1、條件變量

當線程需要等待特定事件發(fā)生、或是某個條件成立時,可以使用條件變量std::condition_variable,它在標準庫頭文件<condition_variable>內(nèi)聲明。

std::mutex mut;
std::queue<data_chunk> data_queue;
std::condition_variable data_cond;
void data_preparation_thread()
{
    while (more_data_to_prepare())
    {
        const data_chunk data = prepare_data();
        std::lock_guard<std::mutex> lk(mut);
        data_queue.push(data);
        data_cond.notify_one();
    }
}
void data_processing_thread()
{
    while (true)
    {
        std::unique_lock<std::mutex> lk(mut);
        data_cond.wait(lk, [] { return !data_queue.empty(); });
        data_chunk data = data_queue.front();
        data_queue.pop();
        lk.unlock();
        process(data);
        if (is_last_chunk(data)) { break; }
    }
}

wait()會先在內(nèi)部調(diào)用lambda函數(shù)判斷條件是否成立,若條件成立則wait()返回,否則解鎖互斥并讓當前線程進入等待狀態(tài)。當其它線程調(diào)用notify_one()時,當前調(diào)用wait()的線程被喚醒,重新獲取互斥鎖并查驗條件,若條件成立則wait()返回(互斥仍被鎖?。?,否則解鎖互斥并繼續(xù)等待。

wait()函數(shù)的第二個參數(shù)可以傳入lambda函數(shù),也可以傳入普通函數(shù)或可調(diào)用對象,也可以不傳。

notify_one()喚醒正在等待當前條件的線程中的一個,如果沒有線程在等待,則函數(shù)不執(zhí)行任何操作,如果正在等待的線程多于一個,則喚醒的線程是不確定的。notify_all()喚醒正在等待當前條件的所有線程,如果沒有正在等待的線程,則函數(shù)不執(zhí)行任何操作。

2、使用future等待一次性事件發(fā)生

C++標準程序庫有兩種future,分別由兩個類模板實現(xiàn),即std::future<>std::shared_future<>,它們的聲明位于頭文件<future>內(nèi)。

2.1、從后臺任務返回值

由于std::thread沒有提供直接回傳結(jié)果的方法,所以我們使用函數(shù)模板std::async()來解決這個問題。std::async()以異步方式啟動任務,并返回一個std::future對象,運行函數(shù)一旦完成,其返回值就由該對象持有。在std::future對象上調(diào)用get()方法時,當前線程就會阻塞,直到std::future準備妥當并返回異步線程的結(jié)果。std::future模擬了對異步結(jié)果的獨占行為,get()僅能被有效調(diào)用一次,調(diào)用時會對目標值進行移動操作。

int find_the_answer_to_ltuae();
void do_other_stuff();
int main()
{
    std::future<int> the_answer = std::async(find_the_answer_to_ltuae);
    do_other_stuff();
    std::cout << "The answer is " << the_answer.get() << std::endl;
}

在調(diào)用std::async()時,它可以接收附加參數(shù)進而傳遞給任務函數(shù)作為其參數(shù),此方式與std::thread的構(gòu)造函數(shù)相同。更多啟動異步線程的方法可參考下面的例程:

struct X
{
    void foo(int, const std::string&);
    std::string bar(const std::string&);
};
X x;
auto f1 = std::async(&X::foo, &x, 42, "hello"); // 調(diào)用p->foo(42, "hello"),p是指向x的指針
auto f2 = std::async(&X::bar, x, "goodbye");    // 調(diào)用tmpx.bar("goodbye"), tmpx是x的拷貝副本
struct Y
{
    double operator()(double);
};
Y y;
auto f3 = std::async(Y(), 3.141);         // 調(diào)用tmpy(3.141),tmpy是由Y()生成的匿名變量
auto f4 = std::async(std::ref(y), 2.718); // 調(diào)用y(2.718)
X baz(X&);
std::async(baz, std::ref(x)); // 調(diào)用baz(x)

我們還能為std::async()補充一個std::launch類型的參數(shù),來指定采用哪種方式運行:std::launch::deferred指定在當前線程上延后調(diào)用任務函數(shù),等到在future上調(diào)用了wait()get(),任務函數(shù)才會執(zhí)行;std::launch::async指定必須開啟專屬的線程,在其上運行任務函數(shù)。該參數(shù)的還可以是std::launch::deferred | std::launch::async,表示由std::async()的實現(xiàn)自行選擇運行方式,這也是這項參數(shù)的默認值。

auto f6 = std::async(std::launch::async, Y(), 1.2); // 在新線程上執(zhí)行
auto f7 = std::async(std::launch::deferred, baz, std::ref(x)); // 在wait()或get()調(diào)用時執(zhí)行
auto f8 = std::async(std::launch::deferred | std::launch::async, baz, std::ref(x)); // 交由實現(xiàn)自行選擇執(zhí)行方式
auto f9 = std::async(baz, std::ref(x));
f7.wait(); // 調(diào)用延遲函數(shù)

2.2、關(guān)聯(lián)future實例和任務

std::packaged_task<>連結(jié)future對象與函數(shù)(或可調(diào)用對象,下同)。std::packaged_task<>對象在執(zhí)行任務時,會調(diào)用關(guān)聯(lián)的函數(shù),把返回值保存為future的內(nèi)部數(shù)據(jù),并令future準備就緒。若一項龐雜的操作能分解為多個子任務,則可以把它們分別包裝到多個std::packaged_task<>實例之中,再傳遞給任務調(diào)度器或線程池,這就隱藏了細節(jié),使任務抽象化,讓調(diào)度器得以專注處理std::packaged_task<>實例,無需糾纏于形形色色的任務函數(shù)。

std::packaged_task<>是類模板,其模板參數(shù)是函數(shù)簽名(例如void()表示一個函數(shù),不接收參數(shù),也沒有返回值),傳入的函數(shù)必須與之相符,即它應接收指定類型的參數(shù),返回值也必須可以轉(zhuǎn)換成指定類型。這些類型不必嚴格匹配,若某函數(shù)接收int類型參數(shù)并返回float值,則可以為其構(gòu)建std::packaged_task<double(double)>的實例,因為對應的類型可以隱式轉(zhuǎn)換。

std::packaged_task<>具有成員函數(shù)get_future(),它返回std::future<>實例,該future的特化類型取決于函數(shù)簽名指定的返回值。std::packaged_task<>還具備函數(shù)調(diào)用操作符,它的參數(shù)取決于函數(shù)簽名的參數(shù)列表。

std::mutex m;
std::deque<std::packaged_task<void()>> tasks;
bool gui_shutdown_message_received();
void get_and_process_gui_message();
void gui_thread()
{
    while (!gui_shutdown_message_received())
    {
        get_and_process_gui_message();
        std::packaged_task<void()> task;
        {
            std::lock_guard<std::mutex> lk(m);
            if (tasks.empty()) { continue; }
            task = std::move(tasks.front());
            tasks.pop_front();
        }
        task();
    }
}
std::thread gui_bg_thread(gui_thread);
template<typename Func>
std::future<void> post_task_for_gui_thread(Func f)
{
    std::packaged_task<void()> task(f);
    std::future<void> res = task.get_future();
    std::lock_guard<std::mutex> lk(m);
    tasks.push_back(std::move(task));
    return res;
}

2.3、創(chuàng)建std::promise

有些任務無法以簡單的函數(shù)調(diào)用表達出來,還有一些任務的執(zhí)行結(jié)果可能來自多個部分的代碼,這時可以借助std::promise顯式地異步求值。配對的std::promisestd::future可以實現(xiàn)下面的工作機制:等待數(shù)據(jù)的線程在future上阻塞,而提供數(shù)據(jù)的線程利用相配的std::promise設(shè)定關(guān)聯(lián)的值,使future準備就緒。

若需從給定的std::promise實例獲取關(guān)聯(lián)的std::future對象,調(diào)用前者的成員函數(shù)get_future()即可,這與std::package_task一樣。promise的值通過成員函數(shù)set_value()設(shè)置,只要設(shè)置好,future即準備就緒,憑借它就能獲取該值。如果std::promise在被銷毀時仍未曾設(shè)置值,保存的數(shù)據(jù)則由異常代替。

void f(std::promise<int> ps)
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    ps.set_value(42);
}

int main()
{
    std::promise<int> ps;
    std::future<int> ft = ps.get_future();
    std::thread t(f, std::move(ps));
    int val = ft.get();
    std::cout << val << std::endl;
    t.join();
}

2.4、將異常保存到future中

若經(jīng)由std::async()調(diào)用的函數(shù)拋出異常,則會被保存到future中,future隨之進入就緒狀態(tài),等到其成員函數(shù)get()被調(diào)用,存儲在內(nèi)的異常即被重新拋出。std::packaged_task也是同理,若包裝的任務函數(shù)在執(zhí)行時拋出異常,則也會被保存到future中,只要調(diào)用get(),該異常就會被再次拋出。自然而然,std::promise也具有同樣的功能,它通過成員函數(shù)顯式調(diào)用實現(xiàn)。假如我們不想保存值,而想保存異常,就不應調(diào)用set_value(),而應調(diào)用成員函數(shù)set_exception()。

2.5、多個線程一起等待

若我們在多個線程上訪問同一個std::future對象,而不采取額外的同步措施,將引發(fā)數(shù)據(jù)競爭并導致未定義的行為。std::future僅能移動構(gòu)造和移動賦值,而std::shared_future的實例則能復制出副本。但即便改用std::shared_future,同一個對象的成員函數(shù)卻依然沒有同步,若我們從多個線程訪問同一個對象,首選方式是:向每個線程傳遞std::shared_future對象的副本,它們?yōu)楦骶€程獨有,這些副本就作為各線程的內(nèi)部數(shù)據(jù),由標準庫正確地同步,可以安全地訪問。

future和promise都具備成員函數(shù)valid(),用于判別異步狀態(tài)是否有效。std::shared_future的實例依據(jù)std::future的實例構(gòu)造而得,前者所指向的異步狀態(tài)由后者決定。因為std::future對象獨占異步狀態(tài),所以若要按默認方式構(gòu)造std::shared_future對象,則須用std::move向其默認構(gòu)造函數(shù)傳遞歸屬權(quán)。

std::promise<int> p;
std::future<int> f(p.get_future());
assert(f.valid());
std::shared_future<int> sf(std::move(f));
assert(!f.valid());
assert(sf.valid());

std::future具有成員函數(shù)share(),直接創(chuàng)建新的std::shared_future對象,并向它轉(zhuǎn)移歸屬權(quán)。

std::promise<std::map<SomeIndexType, SomeDataType, SomeComparator, SomeAllocator>::iterator> p;
auto sf = p.get_future().share();

3、限時等待

有兩種超時機制可供選擇:一是延遲超時,線程根據(jù)指定的時長而繼續(xù)等待;二是絕對超時,在某個特定時間點來臨之前,線程一直等待。大部分等待函數(shù)都有變體,專門處理這兩種機制的超時。處理延遲超時的函數(shù)變體以_for為后綴,而處理絕對超時的函數(shù)變體以_until為后綴。例如std::condition_variable的成員函數(shù)wait_for()wait_until()。文章來源地址http://www.zghlxwxcb.cn/news/detail-694008.html

到了這里,關(guān)于《C++并發(fā)編程實戰(zhàn)》讀書筆記(3):并發(fā)操作的同步的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • C++并發(fā)編程 -3.同步并發(fā)操作

    C++并發(fā)編程 -3.同步并發(fā)操作

    本文介紹如何使用條件變量控制并發(fā)的同步操作、C++ 并發(fā)三劍客,函數(shù)式編程 1.概念 ????????C++條件變量(condition variable)是一種多線程編程中常用的同步機制,用于線程間的通信和協(xié)調(diào)。它允許一個或多個線程等待某個條件的發(fā)生,當條件滿足時,線程被喚醒并繼續(xù)執(zhí)

    2024年02月22日
    瀏覽(21)
  • java并發(fā)編程之美第五章讀書筆記

    java并發(fā)編程之美第五章讀書筆記

    CopyOnWriteArrayList 線程安全的ArrayList,對其進行的修改操作都是在底層的一個復制的數(shù)組(快照)進行的,也就是寫時復制策略 類圖 每一個對象里面有一個array數(shù)組進行存放具體的元素,ReentrantLock獨占鎖對象用來保證同時只有一個線程對array進行修改,這里只要記得ReentrantLock是獨占鎖

    2024年02月03日
    瀏覽(20)
  • 《C++高級編程》讀書筆記(七:內(nèi)存管理)

    《C++高級編程》讀書筆記(七:內(nèi)存管理)

    1、參考引用 C++高級編程(第4版,C++17標準)馬克·葛瑞格爾 2、建議先看《21天學通C++》 這本書入門,筆記鏈接如下 21天學通C++讀書筆記(文章鏈接匯總) 1. 使用動態(tài)內(nèi)存 1.1 如何描繪內(nèi)存 在本書中,內(nèi)存單元表示為一個帶有標簽的框,該標簽表示這個內(nèi)存對應的變量名,方

    2024年02月08日
    瀏覽(57)
  • C++并發(fā)操作解密:輕松搞定數(shù)據(jù)同步

    C++并發(fā)操作解密:輕松搞定數(shù)據(jù)同步

    ? 概述: 在C++中,通過互斥鎖解決并發(fā)數(shù)據(jù)同步問題。定義共享數(shù)據(jù)和互斥鎖,編寫線程函數(shù),使用互斥鎖確保操作的原子性。主函數(shù)中創(chuàng)建并啟動線程,保障線程安全。實例源代碼演示了簡單而有效的同步機制。 在C++中解決并發(fā)操作時的數(shù)據(jù)同步問題通常需要使用互斥鎖

    2024年02月04日
    瀏覽(21)
  • 《Java并發(fā)編程實戰(zhàn)》課程筆記(十二)

    《Java并發(fā)編程實戰(zhàn)》課程筆記(十二)

    對賬系統(tǒng)的業(yè)務簡化后: 首先用戶通過在線商城下單,會生成電子訂單,保存在訂單庫; 之后物流會生成派送單給用戶發(fā)貨,派送單保存在派送單庫。 為了防止漏派送或者重復派送,對賬系統(tǒng)每天還會校驗是否存在異常訂單。 目前對賬系統(tǒng)的處理邏輯是首先查詢訂單,然后

    2024年02月08日
    瀏覽(18)
  • 《Java并發(fā)編程實戰(zhàn)》課程筆記(九)

    《Java并發(fā)編程實戰(zhàn)》課程筆記(九)

    信號量模型還是很簡單的,可以簡單概括為:一個計數(shù)器,一個等待隊列,三個方法。 在信號量模型里,計數(shù)器和等待隊列對外是透明的,所以只能通過信號量模型提供的三個方法來訪問它們,這三個方法分別是:init()、down() 和 up()。 init():設(shè)置計數(shù)器的初始值。 down():計

    2024年02月07日
    瀏覽(21)
  • 《Java并發(fā)編程實戰(zhàn)》課程筆記(二)

    《Java并發(fā)編程實戰(zhàn)》課程筆記(二)

    在單核時代,所有的線程都是在一顆 CPU 上執(zhí)行,CPU 緩存與內(nèi)存的數(shù)據(jù)一致性容易解決。 因為所有線程都是操作同一個 CPU 的緩存,一個線程對緩存的寫,對另外一個線程來說一定是可見的。 一個線程對共享變量的修改,另外一個線程能夠立刻看到,我們稱為可見性。 多核

    2024年02月06日
    瀏覽(22)
  • C++ 并發(fā)編程實戰(zhàn) 第二章 線程管控

    線程通過構(gòu)建 std::thread 對象而自動啟動 ,該對象指明線程要運行的任務。 對應復雜的任務,可以使用函數(shù)對象。 一旦啟動了線程,我們就需明確是要等待它結(jié)束(與之匯合 join() ),還是任由它獨自運行(與之分離 detach() ) ??? 同一個線程的 .join() 方法不能被重復調(diào)用

    2023年04月08日
    瀏覽(24)
  • C++并發(fā)編程 | 原子操作std::atomic

    C++并發(fā)編程 | 原子操作std::atomic

    目錄 1、原子操作std::atomic相關(guān)概念 2、不加鎖情況 3、加鎖情況 ?4、原子操作 5、總結(jié) 原子操作: 更小的代碼片段,并且該片段必定是連續(xù)執(zhí)行的,不可分割。 1.1 原子操作std::atomic與互斥量的區(qū)別 1) 互斥量 :類模板,保護一段共享代碼段,可以是一段代碼,也可以是一個

    2023年04月26日
    瀏覽(25)
  • c++并發(fā)編程實戰(zhàn)-第3章 在線程間共享數(shù)據(jù)

    多線程之間共享數(shù)據(jù),最大的問題便是數(shù)據(jù)競爭導致的異常問題。多個線程操作同一塊資源,如果不做任何限制,那么一定會發(fā)生錯誤。例如: 輸出: 顯然,上面的輸出結(jié)果存在問題。出現(xiàn)錯誤的原因可能是: 某一時刻, th1線程獲得CPU時間片,將g_nResource從100增加至200后時

    2024年02月08日
    瀏覽(13)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包