2016-08-30 9 views
2

になるために、私はboost::asio::strandwrap機能を使用しようとしたとき、NULLになることをshared_from_thisから来たshared_ptrを引き起こすように見えるので、それが二度目に実行された場合、プログラムがクラッシュする原因になります。ブースト:: ASIO ::ストランドshared_from_thisからのshared_ptrを引き起こしラップがNULL

ただし、これはバインドされた関数が引数なしの場合です。

ウォッチポイントを設定して呼び出しスタックをチェックした後、この動作はasync_result_initからBOOST_ASIO_MOVE_CASTに由来するshared_ptrスワッピング値の移動コンストラクタによって発生しているようです。

私の質問は、この問題を避けるためにwrapを正しく使用するにはどうすればよいですか?

class object : public boost::enable_shared_from_this<object> { 
public: 
    object() 
    : service(new boost::asio::io_service), 
    work(new boost::asio::io_service::work(*service)), 
    strand(*service), 
    value(0) {} 

    void workerThread() { 
     service->run(); 
    } 

    void run() { 
     func_int = strand.wrap(boost::bind(&object::handler_int, shared_from_this(), _1)); 
     func_void = strand.wrap(boost::bind(&object::handler_void, shared_from_this())); 

     std::thread thread(boost::bind(&object::workerThread, this)); 

     func_int(1); 
     func_int(1); 

     func_void(); 
     func_void(); // Will crash due to shared_ptr being NULL, hence "value" cannot be accessed in handler_void 

     thread.join(); 
    } 

    void handler_int(int parameter) { 
     cout << "handler_int: " << value << endl; 
    } 

    void handler_void() { 
     cout << "handler_void: " << value << endl; 
    } 

    boost::shared_ptr<boost::asio::io_service> service; 
    boost::shared_ptr<boost::asio::io_service::work> work; 
    boost::asio::strand strand; 
    std::function<void(int)> func_int; 
    std::function<void(void)> func_void; 
    int value; 
}; 


int main(int argc, char *argv[]) {  
    boost::shared_ptr<object> obj(new object()); 
    obj->run(); 
    return 0; 
} 
+0

作業オブジェクトを正しく初期化していません。 –

+0

@RichardHodges:返信いただきありがとうございますが、それが原因であるかどうかはわかりません。私はこのように初期化された「仕事」も見てきました:[リンク](http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/services/logger_service.hpp) – Bill

+0

が理解されています。それは貧弱なスタイルですが、本当の答えは以下のとおりです。 –

答えて

3

これはwrapが何の誤解によるものです:

以下は、私が何を意味するのかをお見せするための簡単なサンプルです。これはあまりにも最近私を捕まえた。私は説明のためにasioの作者に書きました。彼が私に言ったことは私を驚かせた。

wrapは、 'wrapped_handler'を返しますが、あなたとは逆には、カバーの下でディスパッチを実行する関数オブジェクトではありません。

実際には、バインドしたハンドラと、エグゼキュータへの参照を含むオブジェクトです。 Asioオブジェクトは、正しいコンテキストでハンドラを実行するために、非同期操作を完了するときにこの情報を使用します。

紛らわしく、このwrapped_handleroperator()です。これは単にバインドされた関数を実行するだけです。 でなく、の機能を発送または発送します。私の見解では、このoperator()は存在すべきではありません。私はChristopher Kohlhoffにこの見解を郵送しました。私は返事がありませんでした。

あなたは、この置き換えることで、これを証明することができます。これに

func_int(1); 

:バウンドメンバ関数の呼び出しごとに、

service->post(strand->wrap(boost::bind(&object::handler_int, shared_from_this(), 1))); 

を。その後、のを実行中であることをテストできます。

あなたが投稿した/投稿したのはio_serviceであることには関係ありません。彼らはwrapによって連合された鎖にまっすぐ送られるでしょう。

最後に、asioとスタイル。

io_service/strand/workオブジェクトの所有権をshared_ptrで指定できます。これは常に不必要です。 io_serviceは、アプリケーションの基本的なコンポーネントであり、中央のメッセージループです。それは不確定な生涯を持たないでしょう。どちらも作品や鎖ではありません。あなたの寿命を終えると:

service.stop(); 
thread(s).join(); 

すべてが順調です。

+0

このような詳細な説明をありがとうございます。 しかし、私がデバッグしているときに、ブレークポイントでオブジェクト構造を見ると、私は 'dispatcher_'と' handler_'メンバ変数を見たので元の関数を包むラッパーであったと推測しました。また、メモリ内に、私の元の 'object'ポインタのアドレスが複数回現れました。 私が知りたいのは、引数の数が異なる場合と、 'wrap'を適切に使う方法が異なる理由です。 – Bill

+0

'service-> post ...'に関しては、私はそれをテストして、2番目のサービスを作成して 'service2-> post'を呼び出すと、関数はservice2で呼び出されます(スレッドIDでチェックされます)。あなたの例では、あなたは '鎖 'でそれを呼び出さなかったので、それは関連付けられていた'鎖'です。 – Bill

+0

私が知る限り、io_serviceはコピーできないので、渡すことができるかどうかを確認するためにshared_ptrをラップします。したがって、私の視点からは必ずしも必要ではありません。 – Bill

関連する問題