1. 程式人生 > >Boost.Asio c++ 網路程式設計翻譯(6)

Boost.Asio c++ 網路程式設計翻譯(6)

io_service類 你應該已經發現大部分使用Boost.Asio編寫的程式碼都會使用幾個ios_service的例項。ios_service是這個庫裡面最重要的類;它負責和作業系統打交道,等待所有非同步操作的結束,然後為每一個非同步操作呼叫完成處理程式。 如果你選擇用同步的方式來建立你的應用,你不需要考慮我將在這一節向你展示的東西。 你可以用幾種不同的方式來使用io_service。在下面的例子中,我們有3個非同步操作,2個socket連線和一個計時器等待: 有一個io_service和一個處理執行緒的單執行緒例子: io_service service_;
// all the socket operations are handled by service_
ip::tcp::socket sock1(service_);
// all the socket operations are handled by service_
ip::tcp::socket sock2(service_);
sock1.async_connect( ep, connect_handler);
sock2.async_connect( ep, connect_handler);
deadline_timer t(service_, boost::posix_time::seconds(5));
t.async_wait(timeout_handler);
service_.run();
有單個io_service例項和多個處理執行緒的多執行緒例子: io_service service_;
ip::tcp::socket sock1(service_);
ip::tcp::socket sock2(service_);
sock1.async_connect( ep, connect_handler);
sock2.async_connect( ep, connect_handler);
deadline_timer t(service_, boost::posix_time::seconds(5));
t.async_wait(timeout_handler);
for ( int i = 0; i < 5; ++i)
boost::thread( run_service);
void run_service() {
service_.run();
}
有多個io_service例項和多個處理執行緒的多執行緒例子: io_service service_[2];
ip::tcp::socket sock1(service_[0]);
ip::tcp::socket sock2(service_[1]);
sock1.async_connect( ep, connect_handler);
sock2.async_connect( ep, connect_handler);
deadline_timer t(service_[0], boost::posix_time::seconds(5));
t.async_wait(timeout_handler);
for ( int i = 0; i < 2; ++i)
boost::thread( boost::bind(run_service, i));
void run_service(int idx) {
service_[idx].run();
}
首先,要注意你不能擁有多個io_service卻只有一個執行緒。下面的程式碼片段沒有任何意義: for ( int i = 0; i < 2; ++i)
service_[i].run();
上面的程式碼片段沒有意義是因為service_[1].run()需要service_[0].run()先結束。因此,所有由service_[1]處理的非同步操作都需要等待,這顯然不是一個好主意。 在前面的3個方案中,我們在等待3個非同步操作結束。為了解釋之間的不同點,我們假設:過一會操作1完成,然後接著操作2完成。同時我們假設每一個完成處理程式需要1秒鐘來完成執行。 在第一個例子中,我們在一個執行緒中等待三個操作全部完成,只要第1個操作完成,我們呼叫它的完成處理程式。儘管操作2緊接著完成了,操作2的完成處理程式需要在1秒鐘後操作1的完成處理程式完成才被呼叫。 第二個例子,我們在兩個執行緒中等待3個非同步操作結束。當操作1完成時,我們在第一個執行緒中呼叫它的完成處理程式。當操作2完成時,緊接著,我們就在第二個執行緒中呼叫它的完成處理函式(當執行緒1在忙著響應操作1的處理程式時,執行緒2空閒著並且可以迴應任何新進來的操作)。 在第三個例子中,因為操作1是sock1的connect,操作2是sock2的connect,所以應用程式會表現得像第二個例子一樣。執行緒1會處理sock1 connect操作的完成處理程式,執行緒2會處理sock2的connect操作的完成處理程式。然而,如果sock1的connect操作是操作1,deadline_timer t的超時操作是操作2,執行緒1會結束正在處理的sock1 connect操作的完成處理程式。因而,deadline_timer t的超時操作必須等sock1 connect操作的完成處理程式結束(等待1秒鐘),因為執行緒1要處理sock1的連線處理程式和t的超時處理程式。 下面是你需要從前面的例子中學到的: 第一種情況是非常基礎的應用程式。因為是序列的方式,所以當幾個處理程式要被同時呼叫時,你會經常遇到瓶頸。如果一個處理程式需要花費很長的時間來執行,所有隨後的處理程式都不得不等待。 第二種情況是比較適用的應用程式。他是非常強壯的——如果幾個處理程式被同時呼叫了(這是有可能的),它們會在各自的執行緒裡面被呼叫。唯一的瓶頸就是所有的處理執行緒都很忙的同時又有新的處理程式被呼叫。然而,這有快速的解決方式,增加處理執行緒的數目即可。 第三種情況最複雜和最難理解的。你只有在第二種情況不能滿足需求時才使用它。這種情況一般就是當你有成千上萬實時(socket)連線時。你可以認為每一個處理執行緒(執行io_service::run()的執行緒)有它自己的select/epoll迴圈;它等待任何一個socket,然後監控一個讀寫操作,當它發現這種操作時,就執行。大部分情況下,你不需要擔心什麼,唯一你需要擔心的就是當你監控的socket數目以指數級的方式增長時(超過1000個的socket)。在那種情況下,有幾個select/epoll迴圈會增加響應時間。
如果你覺得你成應用程式可能需要轉換到第三種模式,請確保監聽操作的這段程式碼(呼叫io_service::run()的程式碼)和應用程式其他部分是隔離的,這樣你就可以很輕鬆的對其進行更改。 最後,要一直記住如果沒有其他需要監控的操作,.run()就會結束,就像下面給的程式碼片段: io_service service_;
tcp::socket sock(service_);
sock.async_connect( ep, connect_handler);
service_.run();
在上面的例子,只要sock建立了一個連線,connect_handler就會被呼叫,然後接著service_.run()就會完成執行。 如果你想要service_.run()接著執行,你需要分配更多的工作給它。這裡有兩個方式來完成這個目標。一種方式是在connect_handler中啟動另外一個非同步操作來分配更多的工作。 另一種方式模擬一些工作給它,用下面的程式碼片段: typedef boost::shared_ptr<io_service::work> work_ptr;
work_ptr dummy_work(new io_service::work(service_));
上面的程式碼可以保證service_.run()一直執行直到你呼叫useservice_.stop()或者 dummy_work.reset(0);// 銷燬 dummy_work. 總結 Boost.Asio是一個複雜的庫,但是卻讓網路程式設計變得異常簡單。編譯它很簡單。它避免使用巨集,這一點做得很好;他雖然定義了少部分的巨集來做選項開關,但是你需要擔心的很少。 Bosot.Asio支援同步和非同步程式設計。他們有很大不同;你需要早早地選擇其中的一種,因為它們之間的轉換是非常複雜而且易錯的。 如果你選擇同步,你可以選擇異常處理或者錯誤碼,從異常處理轉到錯誤碼;只需要在call函式中增加一個引數即可(錯誤碼)。 Boost.Asio不僅僅可以用來做網路程式設計。它還有其他更多的特性,這讓它顯得更有價值,比如訊號量,計時器等等。 下一章我們將深入研究大多數Boost.Asio中用來做網路程式設計的函式和類。同時我們也會學一些非同步程式設計的訣竅。