asio學習筆記8——stackfull coroutine

網事已瘋發表於2014-10-27

blog中的文章地址:http://www.godebug.org/index.php/archives/126/

今天沒事可幹,就再寫一篇吧啊哈哈哈 這個spawn是boost 1.54中的asio新增加的東西,我目前用的boost是1.56,如果不能用以下程式碼請更新boost.... 其實有了之前介紹的asio中的stackless coroutinestackfull的boost.coroutine,這裡也就沒啥原理性的東西介紹了,asio到底怎麼封裝的我也沒看明白,不過只要好用就行了。簡單說一下幾個類和函式的作用就可以了,剩下的太簡單了,簡直就跟迴歸了同步的網路程式設計一個樣了。 還是直接上程式碼:

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <memory>
#include <functional>
#include <iostream>


class session : public std::enable_shared_from_this < session >
{
public:
    explicit session(boost::asio::io_service& io_service)
        : strand_(io_service), socket_(io_service)
    {
    }

    boost::asio::ip::tcp::socket& socket()
    {
        return socket_;
    }

    void go()
    {
        boost::asio::spawn(strand_,
            std::bind(&session::echo,
            shared_from_this(), std::placeholders::_1));
    }

private:
    void echo(boost::asio::yield_context yield)
    {
        char data[128];
        for (;;)
        {
            std::size_t n = socket_.async_read_some(boost::asio::buffer(data), yield);
            boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield);
        }
    }

    boost::asio::io_service::strand strand_;
    boost::asio::ip::tcp::socket socket_;
};

void do_accept(boost::asio::io_service& io_service,
    unsigned short port, boost::asio::yield_context yield)
{
    boost::asio::ip::tcp::acceptor acceptor(
        io_service,
        boost::asio::ip::tcp::endpoint(
        boost::asio::ip::tcp::v4(), port));

    for (;;)
    {
        boost::system::error_code ec;
        std::shared_ptr<session> new_session(std::make_shared<session>(io_service));
        acceptor.async_accept(new_session->socket(), yield[ec]);
        if (!ec)
        {
            new_session->go();
        }
    }
}

int main(int argc, char* argv[])
{
    try
    {
        boost::asio::io_service io_service;

        boost::asio::spawn(io_service,
            std::bind(do_accept,
            std::ref(io_service), 8888, std::placeholders::_1));

        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

把asio的example\cpp03\spawn中的例子稍微修改了下。簡單解釋下,boost::asio::spawn就是開啟了一個stackfull的coroutine,這個函式需要一個回撥函式(比如說do_accept),然後會傳一個boost::asio::yield_context給這個函式,只要用這個yield_context作為handle來呼叫asio的非同步函式,就可以把這些函式程式設計“同步”的。不過這個“同步”是加引號的,雖然這個名叫非同步,但是是同步的操作在沒有完成之前是不會返回的,但是在內部會繼續執行其他的非同步操作,並不會真的阻塞。於是我們現在就同時有了一部和同步的優勢,既不會因為同步喪失效能,也不會像原來的非同步那樣一個接一個的回撥,寫的都要吐了。boost::asio::io_service::strand我理解的就是總是把io_service傳來傳去不方便,是用來代替io_service給spawn開coroutine的。

回想一下之前用asio非同步要迴圈幹某件事的一般做法,至少需要單獨定義兩個函式,一個用來發起非同步操作,比如說呼叫boost::asio::async_read,第二個函式用來做事件完成的handle,然後在handle中再次呼叫發起非同步操作的那個函式,這樣就構成了一個迴圈。但是現在我們只需要用c++本身的迴圈語句即可搞定,就像上面的do_accept。

另外就是1.當一個非同步讀取/寫入函式用了yield當handler,它的返回值一般是讀取或者寫入的位元組數;2.yield過載了[],可以接受一個boost::system::error_code作引數,返回出錯資訊的。如果不傳入這個error_code,函式會把他當異常丟擲來,這些可以很明顯的從示例程式碼中看出來。

over

相關文章