2016-04-13 2 views
0

これは私のアプリケーションで問題が再現する比較的単純なプログラムです:私はLinuxマシン上でそれを実行すると不適切なスレッドコンテキストでshared_ptrが破壊されないようにするにはどうすればよいですか?

#include <boost/asio.hpp> 
#include <boost/asio/steady_timer.hpp> 
#include <boost/noncopyable.hpp> 
#include <boost/thread.hpp> 
#include <chrono> 
#include <functional> 
#include <iostream> 
#include <memory> 

class worker : boost::noncopyable { 
public: 
    explicit worker(boost::asio::io_service& io); 
    ~worker(); 
    void just_do_it(const std::function<void()>& when_done); 

private: 
    boost::asio::io_service& io_; 
    boost::asio::io_service worker_io_; 
    boost::thread thread_; 
}; 

worker::worker(boost::asio::io_service& io) 
: io_(io) 
{ 
    thread_ = boost::thread([this] { 
     boost::asio::io_service::work my_work(worker_io_); 
     worker_io_.run(); 
    }); 
} 

worker::~worker() 
{ 
    worker_io_.stop(); 
    std::clog << "join...\n"; 
    thread_.join(); 
} 

void worker::just_do_it(const std::function<void()>& when_done) 
{ 
    worker_io_.post([this, when_done] { 
     io_.post(when_done); 
     boost::asio::steady_timer(worker_io_, std::chrono::seconds(1)).wait(); 
    }); 
} 

int main() 
{ 
    boost::asio::io_service io; 
    boost::asio::steady_timer timer(io, std::chrono::seconds(5)); 
    timer.async_wait(std::bind([] { std::clog << "terminating...\n"; })); 
    { 
     auto my_worker = std::make_shared<worker>(io); 
     my_worker->just_do_it([my_worker] { 
      std::clog << "did it\n"; 
      my_worker->just_do_it([my_worker] { 
       std::clog << "did it second time\n"; 
       // now my_worker is not needed and we allow it to die 
      }); 
     }); 
    } 
    io.run(); 
} 

を私が参照してください。

それが原因のshared_ptrのクラッシュ
did it 
did it second time 
join... 
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::thread_resource_error> >' 
    what(): boost thread: trying joining itself: Resource deadlock avoided 
Aborted 

はworker`に労働者のデストラクタを呼び出しますのスレッド。私はこのように修正することができます:

std::shared_ptr<worker> holder; 
{ 
    holder = std::make_shared<worker>(io); 
    holder->just_do_it([&holder] { 
     std::clog << "did it\n"; 
     holder->just_do_it([&holder] { 
      std::clog << "did it second time\n"; 
      // now worker is not needed and we destroy it 
      holder.reset(); 
     }); 
    }); 
} 

しかし、これは手動でのオブジェクトの寿命管理です。それは新しいものを使用して削除するよりもあまり良くありません。それを避ける方法はありますか?

+0

とにかく後でスレッドに参加したいですか?だから、あなたはそれの外側のスレッドへのハンドルが必要です。 –

+0

スレッドはワーカークラスメンバ変数を使用できるので、ワーカーを破壊する前にスレッドを停止する必要があります。 –

+0

あなたの解決策は問題のあるコードよりも自動ではありませんが、ポインタが破棄されたときに変更するだけです(例外的に手動の 'new' /' delete'よりも優れています)。あなたは別の解決策を考えることができますが、あなたが提供するものは最も簡単なIMOのようです。 – piwi

答えて

1

threadオブジェクトは、スレッドの有効期間を管理するコードが所有している必要があります。

現在、threadの有効期間はworkerクラスによって管理され、workerクラスの有効期間は実行スレッドによって管理されます。

これは非常に基本的な管理ループです。

ワーカーの破壊の意味も非常に混乱しています。あなたは共有ポインタを持っており、最後の共有ポインタが破壊されたときに、操作は未知のタスクをどこかで終了しているスレッドでブロックされます。参照カウント操作がブロックするものがこのシステムではほとんど不可能になると予測します。


1つのアプローチは、ワーカー内のワーカーに弱いポインタを取り込むことです。次に、ワーカースレッドのライフタイムを管理するコードは、すべてがラップアップされるときに選択されます。共有ポインタがなくなったとき。


一般に、スレッドは外部エンティティによって管理される必要があります。

有用な方法として、名前付きタスクキューを持つスレッドプールがあります。各タスクキューは順番に実行されるため、スレッドのように動作しますが、キュー内のタスクが同じスレッドで実行される保証はなく、スレッドが待機することを保証しませんキュー内の新しいタスク。

名前キューにはGUIDのようなものを使用することも、要求に応じて生成されるよりグローバルではない一意の識別子を使用することもできます(newのような何らかのポインタ)。 2番目のケースでは、.join()呼び出しに相当する呼び出しがスレッドキューIDを破棄しています。

これにより、生のboostスレッディングプリミティブを使用しなくなります。しかし私の経験では、stdスタイルのプリミティブは、pthreadsスタイルのスレッドを使用してステップアップされていますが、クライアントコードで実際に直接使用したいものとはまだまだ離れています。

関連する問題