亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

C++ 并發編程的一種思維

2019-11-14 11:11:09
字體:
來源:轉載
供稿:網友

愈發緊迫的挑戰

現今,單臺機器擁有多個獨立的計算單元已經太常見了,這點在服務器的處理器上表現尤為明顯,據 AMD 的一張 2012-2013 服務器路線圖顯示,服務器處理器的核心數將在 2013 年達到 20 顆之多。合理的利用 CPU 資源已是一個不得不考慮的問題。不少 C++ 程序員依然使用著多線程模型,但是對多線程的掌控并不是一件容易的事情,開發中容易出錯、難以調試。有些開發者為了避免多線程帶來的復雜度而棄用多線程,有些開發者則另投其他語言陣營,例如 Erlang。其實我們還有其他的選擇,Theron 就是其中之一。

什么是 Theron?

Theron 是一個用于并發編程的 C++ 庫(http://www.theron-library.com/),通過 Theron 我們可以避免多線程開發中各種痛處,例如:共享內存、線程同步。Theron 通過 Actor 模型向我們展示了另一種思維。

什么是 Actor 模型?

Erlang 因為其優秀的并發特性而被大家所關注,而其并發特性的關鍵之一就是在于其采用了 Actor 模型(http://c2.com/cgi/wiki?ErlangLanguage)。與 Actor 模型相對應的模型則是我們在面向對象編程中使用的 Object 模型,Object 模型中宣揚,一切皆為 Object(對象),而 Actor 模型則認為一切皆為 Actor。Actor 模型中,Actor 之間通過消息相互通信,這是其和 Object 模型的一個顯著的區別,換而言之 Actor 模型使用消息傳遞機制來代替了 Object 模型中的成員方法調用。這樣做意義重大,因為相對于成員方法的調用來說,消息的發送是非阻塞的,它無需等待被調用方法執行完成就可以返回,下圖顯示了此種區別:

A::a() 調用了 objB.b(),此時 A::a() 必須等待 B::b() 的返回才能繼續執行。在 Actor 模型中,對應的做法是 Actor A 向 Actor B 發送消息并立即返回,這時候 Actor A 可以繼續執行下去,與此同時 Actor B 收到消息被喚醒并和 Actor A 并行執行下去。

Theron 中的每個 Actor 都會綁定一個唯一的地址,通過 Actor 的地址就可以向其發送消息了,每個 Actor 都有一個消息隊列。從編碼者的角度看來,每實例化一個 Actor 都創建了一個和 Actor 相關的“線程”(非系統級的線程)。每個 Actor 總是被單線程的執行。總結來說 Theron 的并發特性的關鍵就在于:每個 Actor 在屬于自己的單個“線程”中執行,而多個 Actor 并發執行。

Hello Theron

相關廠商內容

如何更敏捷的使用 Golang 開發微服務

相關贊助商

QCon北京2017,4月16-18日,北京·國家會議中心,精彩內容搶先看

在談及更多內容之前,我們先來看看 Theron 的一個簡單的范例,借以獲得一個最直觀的印象。在 http://www.theron-library.com/ 可以下載到 Theron 的最新版,Theron 提供了 makefile 便于 gcc 用戶編譯,同時其也為 Windows 用戶提供了 Visual Studio solution 文件 Theron.sln 用于構建 Theron。編譯 Theron 很容易,不會有太多的障礙,需要注意的是構建 Theron 需要指定依賴的線程庫,Theron 支持三種線程庫:std::thread(C++11 標準線程庫)、Boost.Thread 和 Windows threads。使用 makefile 構建時,通過 threads 參數指定使用的線程庫(更為詳細的信息參考:http://www.theron-library.com/index.php?t=page&p=gcc),使用 Visual Studio 構建時,通過選擇適當的 Solution configuration 來指定使用的線程庫(更為詳細的信息參考:http://www.theron-library.com/index.php?t=page&p=visual studio)。下面我們來看一個最簡單的范例:

#include <stdio.h>#include <Theron/Framework.h>#include <Theron/Actor.h>// 定義一個消息類型// 在 Theron 中,任何類型都可以作為一個消息類型// 唯一的一個約束是消息類型的變量能夠被拷貝的// 消息按值發送(而非發送它們的地址)struct StringMessage{    char m_string[64];};// 用戶定義的 Actor 總需要繼承于 Theron::Actor// 每個 Actor 和應用程序的其他部分通信的唯一途徑就是通過消息class Actor : public Theron::Actor{public:    inline Actor()    {        // 注冊消息的處理函數        RegisterHandler(this, &Actor::Handler);     }PRivate:    // 消息處理函數的第一個參數指定了處理的消息的類型    inline void Handler(const StringMessage& message, const Theron::Address from)     {         printf("%s/n", message.m_string);        if (!Send(message, from))             printf("Failed to send message to address %d/n", from.AsInteger());    } };int main(){    // Framework 對象用于管理 Actors    Theron::Framework framework;    // 通過 Framework 構建一個 Actor 實例并持有其引用    // Actor 的引用類似于 java、C# 等語言中的引用的概念    // Theron::ActorRef 采用引用計數的方式實現,類似于 boost::shared_ptr    Theron::ActorRef simpleActor(framework.CreateActor<Actor>());    // 創建一個 Receiver 用于接收 Actor 發送的消息    // 用于在非 Actor 代碼中(例如 main 函數中)與 Actor 通信    Theron::Receiver receiver;    // 構建消息    StringMessage message;    strcpy(message.m_string, "Hello Theron!");    // 通過 Actor 的地址,我們就可以向 Actor 發送消息了    if (!framework.Send(message, receiver.GetAddress(), simpleActor.GetAddress()))        printf("Failed to send message!/n");    // 等到 Actor 發送消息,避免被關閉主線程    receiver.Wait();    return 0;}

這個范例比較簡單,通過 Actor 輸出了 Hello Theron。需要額外說明的一點是消息在 Actor 之間發送時會被拷貝,接收到消息的 Actor 只是引用到被發送消息的一份拷貝,這么做的目的在于避免引入共享內存、同步等問題。

Theron 的消息處理

前面談到過,每個 Actor 都工作在一個屬于自己的“線程”上,我們通過一個例子來認識這一點,我們修改上面例子中的 Actor:: Handler 成員方法:

inline void Handler(const StringMessage& message, const Theron::Address from) {     while (true)    {        printf("%s --- %d/n", message.m_string, GetAddress().AsInteger());#ifdef _MSC_VER        Sleep(1000);#else        sleep(1);#endif    }}

此 Handler 會不斷的打印 message 并且帶上當前 Actor 的地址信息。在 main 函數中,我們構建兩個 Actor 實例并通過消息喚醒它們,再觀察輸出結果:

Hello Theron! --- 1Hello Theron! --- 2Hello Theron! --- 2Hello Theron! --- 1Hello Theron! --- 2Hello Theron! --- 1Hello Theron! --- 2Hello Theron! --- 1......

這和我們預期的一樣,兩個 Actor 實例在不同的線程下工作。實際上,Framework 創建的時候會創建系統級的線程,默認情況下會創建兩個(可以通過 Theron::Framework 構造函數的參數決定創建線程的數量),它們構成一個線程池,我們可以根據實際的 CPU 核心數來決定創建線程的數量,以確保 CPU 被充分利用。線程池的線程是以何種方式進行調度的?如下圖:

接收到消息的 Actor 會被放置于一個線程安全的 Work 隊列中,此隊列中的 Actor 會被喚醒的工作線程取出,并進行消息的處理。這個過程中有兩個需要注意的地方:

對于某個 Actor 我們可以為某個消息類型注冊多個消息處理函數,那么此消息類型對應的多個消息處理函數會按照注冊的順序被串行執行下去線程按順序處理 Actor 收到的消息,一個消息未處理完成不會處理消息隊列中的下一個消息 我們可以想象,如果存在三個 Actor,其中兩個 Actor 的消息處理函數中存在死循環(例如上例中的 while(true)),那么它們一旦執行就會霸占兩條線程,若線程池中沒有多余線程,那么另一個 Actor 將被“餓死”(永遠得不到執行)。我們可以在設計上避免這種 Actor 的出現,當然也可以適當的調整線程池的大小來解決此問題。Theron 中,線程池中線程的數量是可以動態控制的,線程利用率也可以測量。但是務必注意的是,過多的線程必然導致過大的線程上下文切換開銷。

一個詳細的例子

我們再來看一個詳細的例子,借此了解 Theron 帶來的便利。生產者消費者的問題是一個經典的線程同步問題,我們來看看 Theron 如何解決這個問題:

#include <stdio.h>#include <Theron/Framework.h>#include <Theron/Actor.h>const int PRODUCE_NUM = 5;class Producer : public Theron::Actor{public:    inline Producer(): m_item(0)    {        RegisterHandler(this, &Producer::Produce);     }private:    // 生產者生產物品    inline void Produce(const int& /* message */, const Theron::Address from)     {         int count(PRODUCE_NUM);        while (count--)        {            // 模擬一個生產的時間#ifdef _MSC_VER            Sleep(1000);#else            sleep(1);#endif            printf("Produce item %d/n", m_item);            if (!Send(m_item, from))                printf("Failed to send message!/n");            ++m_item;        }    }     // 當前生產的物品編號    int m_item;};class Consumer : public Theron::Actor{public:    inline Consumer(): m_consumeNum(PRODUCE_NUM)    {        RegisterHandler(this, &Consumer::Consume);    }private:    inline void Consume(const int& item, const Theron::Address from)    {        // 模擬一個消費的時間#ifdef _MSC_VER        Sleep(2000);#else        sleep(2);#endif        printf("Consume item %d/n", item);        --m_consumeNum;        // 沒有物品可以消費請求生產者進行生產        if (m_consumeNum == 0)        {            if (!Send(0, from))                printf("Failed to send message!/n");            m_consumeNum = PRODUCE_NUM;        }    }    int m_consumeNum;};int main(){    Theron::Framework framework;    Theron::ActorRef producer(framework.CreateActor<Producer>());    Theron::ActorRef consumer(framework.CreateActor<Consumer>());    if (!framework.Send(0, consumer.GetAddress(), producer.GetAddress()))        printf("Failed to send message!/n");    // 這里使用 Sleep 來避免主線程結束    // 這樣做只是為了簡單而并不特別合理    // 在實際的編寫中,我們應該使用 Receiver#ifdef _MSC_VER    Sleep(100000);#else    sleep(100);#endif    return 0;}

生產者生產物品,消費者消費物品,它們并行進行,我們沒有編寫創建線程的代碼,沒有構建共享內存,也沒有處理線程的同步。這一切都很輕松的完成了。

代價和設計

和傳統的多線程程序相比 Theron 有不少優勢,通過使用 Actor,程序能夠自動的并行執行,而無需開發者費心。Actor 總是利用消息進行通信,消息必須拷貝,這也意味著我們必須注意到,在利用 Actor 進行并行運算的同時需避免大量消息拷貝帶來的額外開銷。

Actor 模型強調了一切皆為 Actor,這自然可以作為我們使用 Theron 的一個準則。但過多的 Actor 存在必然導致 Actor 間頻繁的通信。適當的使用 Actor 并且結合 Object 模型也許會是一個不錯的選擇,例如,我們可以對系統進行適當劃分,得到一些功能相對獨立的模塊,每個模塊為一個 Actor,模塊內部依然使用 Object 模型,模塊間通過 Actor 的消息機制進行通信。

Theron 的未來

Theron 是個有趣的東西,也許你沒有使用過它,你也不了解 Actor 模型,但是 Actor 的思想卻不新鮮,甚至你可能正在使用。目前來說,我還沒有找到 Theron 在哪個實際的商業項目中使用,因此對 Theron 的使用還存在一些未知的因素。還有一些特性,諸如跨主機的分布式的并行執行是 Theron 不支持的,這些都限制了 Theron 的使用,不過作者正在積極的改變一些東西(例如,作者表示會在今后添加 Remote Actors)。無論 Theron 未來如何,Theron 以及 Actor 模型帶來的思想會讓我們更加從容面對多核的挑戰。

作者簡介

梁國棟,熱愛編程,熱衷于撰寫技術類文章,精益思想倡導者,UNIX 哲學實踐者,專注于高性能服務器程序的研發多年,目前負責網游服務器的研發工作。


感謝李永倫對本文的審校。

給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至editors@cn.infoq.com。也歡迎大家通過新浪微博(@InfoQ)或者騰訊微博(@InfoQ)關注我們,并與我們的編輯和其他讀者朋友交流。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲无av在线中文字幕| 精品偷拍一区二区三区在线看| 亚洲国产精品一区二区久| 日韩av最新在线| 国产精品黄页免费高清在线观看| 69av成年福利视频| 亚洲尤物视频网| 欧美日韩国产在线看| 亚洲视屏在线播放| 久久夜精品香蕉| 久久久电影免费观看完整版| 欧美精品生活片| 久久综合色影院| 欧美最猛性xxxxx(亚洲精品)| 国产精品一区专区欧美日韩| 91亚洲精品久久久久久久久久久久| 久久久精品免费视频| 久久频这里精品99香蕉| 亚洲天堂av女优| 国产成人免费av电影| 97人人做人人爱| 亚洲欧美激情视频| 欧美极品美女视频网站在线观看免费| 91精品一区二区| 日本免费久久高清视频| 成人福利网站在线观看| 亚洲天堂第一页| 亚洲欧洲在线播放| 亚洲综合小说区| 97免费在线视频| 亚洲成人激情视频| 亚洲男人天堂古典| 久久好看免费视频| 91在线视频免费| 国产免费久久av| 日韩中文在线视频| 国产精品入口夜色视频大尺度| 久操成人在线视频| 亚洲性视频网站| 亚洲精品福利免费在线观看| 久久久av免费| 亚洲成人网av| 欧美激情一二三| 日韩精品欧美国产精品忘忧草| 日本久久久a级免费| 性日韩欧美在线视频| 国产精品中文在线| 97在线视频免费播放| 国产一区二区三区丝袜| 91chinesevideo永久地址| 亚洲欧美第一页| 国产亚洲欧美aaaa| 欧美猛交免费看| 国产高清在线不卡| 在线观看日韩专区| 中文字幕欧美日韩在线| 亚洲一二三在线| 亚洲综合视频1区| 欧美成人亚洲成人| 国产成人精品视频在线| www.亚洲男人天堂| 欧美视频在线观看免费| 91精品免费视频| 国产成人自拍视频在线观看| 国产精品成人久久久久| 亚洲最大激情中文字幕| 韩国19禁主播vip福利视频| 中文字幕久久精品| 欧美亚洲成人精品| 久久婷婷国产麻豆91天堂| 亚洲精品视频在线观看视频| 欧美日韩在线第一页| 国产精品ⅴa在线观看h| 日韩av网站电影| 亚洲欧洲在线看| 欧美综合激情网| 中文字幕亚洲欧美日韩高清| 在线观看视频99| 亚洲国产欧美久久| 国产日韩精品综合网站| 精品偷拍一区二区三区在线看| xvideos成人免费中文版| 日韩成人在线视频网站| 亚洲人精选亚洲人成在线| 成人有码在线播放| 精品视频在线播放色网色视频| 成人精品一区二区三区电影免费| 欧美黑人视频一区| 日韩美女在线观看| 亚洲aa在线观看| 97视频在线观看视频免费视频| 国产精品精品久久久久久| 欧美在线性爱视频| 日韩av中文在线| 在线成人激情视频| 蜜臀久久99精品久久久无需会员| 亚洲成年人在线| 91精品视频免费| 国产精品美女久久久久久免费| 国内精品久久久久久久久| 日韩国产一区三区| 亚洲免费视频一区二区| 91在线观看免费高清完整版在线观看| 日韩精品久久久久久久玫瑰园| 久久久久久久国产精品视频| 中文字幕精品www乱入免费视频| 国产在线观看不卡| 日韩视频―中文字幕| 亚洲精品国产综合区久久久久久久| 久久久这里只有精品视频| 国产91在线播放精品91| 久久久久久久亚洲精品| 亚洲精品久久久久久久久久久久久| 欧美激情a∨在线视频播放| 日韩网站免费观看高清| 午夜精品www| 国产精品女人网站| 亚洲最大福利网| 98精品国产自产在线观看| 在线激情影院一区| 国产精品久久91| 欧美成人午夜剧场免费观看| 欧美另类第一页| 国产精品av在线| 成人激情视频免费在线| 欧美日韩福利电影| 久久综合久久八八| 一区二区三区四区精品| 久久精品视频一| 欧美性猛交xxxx偷拍洗澡| 成人国内精品久久久久一区| 97超级碰碰人国产在线观看| 欧洲成人性视频| 国产欧美一区二区三区在线看| 日韩欧美中文免费| www国产精品视频| 国产九九精品视频| 成人在线播放av| yellow中文字幕久久| 亚洲最大成人免费视频| 国产精品福利在线观看网址| 亚洲精品资源在线| 俺去亚洲欧洲欧美日韩| 97国产在线观看| 一区二区日韩精品| 色老头一区二区三区在线观看| 亚洲福利在线播放| 成人久久久久久| 国产精品视频yy9099| 国产深夜精品福利| 91午夜在线播放| 久久夜精品va视频免费观看| 国产成人精品一区| 国产成人亚洲综合91精品| 色一情一乱一区二区| 91中文精品字幕在线视频| 亚洲综合在线中文字幕| 欧美与欧洲交xxxx免费观看| 色偷偷88888欧美精品久久久| 亚洲第一av网站| 亚洲精品自拍偷拍| 亚洲精品日韩在线| 亚洲天堂第一页| 亚洲色图50p|