web-dev-qa-db-ja.com

C ++ 0xスレッド割り込み

C++ 0x最終ドラフトによると、スレッドの終了を要求する方法はありません。ただし、必要に応じて、日曜大工ソリューションを実装する必要があります。

一方、boost :: threadは、スレッドをsafe方式で中断するメカニズムを提供します。

あなたの意見では、最良の解決策は何ですか?独自の協力的な「割り込みメカニズム」を設計するか、ネイティブに移行しますか?

55
Nicola Bonelli

すべての言語仕様は、サポートは言語に組み込まれていないと述べています。 boost::thread::interruptもスレッド関数からのサポートが必要です:

中断されたスレッドが次に指定された中断ポイントの1つを実行するとき(または実行中に現在ブロックされている場合)

すなわち、スレッド関数が呼び出し側に割り込みの機会を与えないとき、あなたはまだ立ち往生しています。

「ネイティブになる」とはどういう意味かわかりません-boost:threadsに魅了されない限り、ネイティブサポートはありません。

それでも、私は明示的なメカニズムを使用します。とにかく十分な中断ポイントがあることを考える必要があります、なぜそれらを明示的にしませんか?私の経験では通常、余分なコードはわずかですが、いくつかの待機を単一オブジェクトから複数オブジェクトに変更する必要があるかもしれません。


「制御フローに例外を使用しない」という方法もありますが、スレッドをいじるのと比較すると、これは単なるガイドラインです。

16
peterchen

ネイティブハンドルを使用してスレッドをキャンセルすることは、スタックに割り当てられたすべてのオブジェクトを破棄する必要があるため、C++では不適切なオプションです。これが、キャンセル操作が含まれない主な理由です。

Boost.Threadは、待機メカニズムでプールする必要がある割り込みメカニズムを提供します。これは一般的なメカニズムとして高価になる可能性があるため、標準には含まれていません。

自分で実装する必要があります。自分でこれを実装する方法に関する同様の質問に対する私の答え here を参照してください。ソリューションを完了するには、interruptedがtrueのときに割り込みをスローし、スレッドがこの割り込みをキャッチして終了する必要があります。

10

その時点で作業していたデータ構造の状態を制御できないため、スレッドを終了することは安全ではありません。

実行中のスレッドを中断する場合は、独自のメカニズムを実装する必要があります。それが必要な場合、デザインは複数のスレッドに対応していません。

スレッドが終了するのを待ちたい場合は、join()またはfutureを使用します。

6
user328543

その時点以降、プロセス全体の状態が不確定になるため、スレッドをプリエンプティブに終了することは安全ではありません。スレッドは、終了する前にクリティカルセクションを取得した可能性があります。そのクリティカルセクションはリリースされません。ヒープが永続的にロックされるなどの可能性があります。

boost::thread::interrupt解決策は、適切に尋ねることによって機能します。 Boost.Thread条件変数で待機するなど、割り込み可能な何かを行うスレッドに割り込みをかけるか、割り込みが呼び出された後にスレッドがこれらのいずれかを行う場合のみ。それでも、スレッドは、たとえばWin32のTerminateThread関数のように、肉挽き器に不意に置かれることはありません。例外を誘発するだけです。 、自動的にクリーンアップし、スレッドを正常に終了します。

6
Marcelo Cantos

日曜大工のソリューションを実装することは最も理にかなっており、実際にはそれほど難しくないはずです。同期的に読み取り/書き込みを行う共有変数が必要になります。これは、スレッドが終了するように要求されているかどうかを示し、スレッドは安全に中断できる状態にあるときにこの変数から定期的に読み取ります。スレッドを中断したい場合は、この変数に同期的に書き込むだけで、スレッドに参加できます。適切に連携していると仮定すると、変数が書き込まれ、シャットダウンされたことに気付くはずです。その結果、結合機能がブロックされなくなります。

ネイティブに移行する場合、それによって何も得られません。標準のクロスプラットフォームOOPスレッド化メカニズムのすべての利点を捨てるだけです。コードを正しくするには、スレッドは協調してシャットダウンする必要があります。上記のとおり。

これは、スレッドキャンセラの簡単な実装です(C++ 0x用)。役に立つことを願っています。

// Class cancellation_point
#include <mutex>
#include <condition_variable>

struct cancelled_error {};

class cancellation_point
{
public:
    cancellation_point(): stop_(false) {}

    void cancel() {
        std::unique_lock<std::mutex> lock(mutex_);
        stop_ = true;
        cond_.notify_all();
    }

    template <typename P>
    void wait(const P& period) {
        std::unique_lock<std::mutex> lock(mutex_);
        if (stop_ || cond_.wait_for(lock, period) == std::cv_status::no_timeout) {
            stop_ = false;
            throw cancelled_error();
        }
    }
private:
    bool stop_;
    std::mutex mutex_;
    std::condition_variable cond_;
};


// Usage example
#include <thread>
#include <iostream>

class ThreadExample
{
public:
    void start() {
        thread_ = std::unique_ptr<std::thread>(
            new std::thread(std::bind(&ThreadExample::run, this)));
    }
    void stop() {
        cpoint_.cancel();
        thread_->join();
    }
private:
    void run() {
        std::cout << "thread started\n";
        try {
            while (true) {
                cpoint_.wait(std::chrono::seconds(1));
            }
        } catch (const cancelled_error&) {
            std::cout << "thread cancelled\n";
        }
    }
    std::unique_ptr<std::thread> thread_;
    cancellation_point cpoint_;
};

int main() {
    ThreadExample ex;
    ex.start();
    ex.stop();
    return 0;
}
5
dimitri

私のスレッドの実装はpimplイディオムを使用し、Implクラスには、サポートするOSごとに1つのバージョンがあり、boostを使用するバージョンもあるため、プロジェクトのビルド時に使用するバージョンを決定できます。

私は2つのクラスを作成することにしました。1つはスレッドで、基本的なOS提供のサービスのみがあります。もう1つはSafeThreadで、これはThreadから継承し、共同で中断するためのメソッドを持っています。

スレッドには、侵入的な終了を行うterminate()メソッドがあります。これは、SafeThreadでオーバーロードされる仮想メソッドであり、イベントオブジェクトを通知します。実行中のスレッドが時々呼び出す必要がある(静的な)yeld()メソッドがあります。このメソッドは、イベントオブジェクトが通知されているかどうかを確認し、通知されている場合は、スレッドエントリポイントの呼び出し元でキャッチされた例外をスローして、スレッドを終了します。そうすると、2番目のイベントオブジェクトに信号を送るため、terminate()の呼び出し元は、スレッドが安全に停止されたことを知ることができます。

デッドロックのリスクがある場合、SafeThread :: terminate()はタイムアウトパラメータを受け入れることができます。タイムアウトが経過すると、Thread :: terminate()を呼び出して、侵入的にスレッドを強制終了します。これは、制御できないもの(サードパーティAPIなど)がある場合、またはデッドロックがリソースリークなどよりも大きなダメージを与える状況にある場合の最後のリソースです。

これがあなたの決定に役立ち、私のデザインの選択について十分に明確な写真を提供してくれることを願っています。そうでない場合は、コードフラグメントを投稿して、必要に応じて明確にすることができます。

4
Fabio Ceconello

この決定に同意します。たとえば、.NETではワーカースレッドを中止できますが、この機能を使用することはなく、プロのプログラマーにこれを行うことはお勧めしません。私は、ワーカースレッドが中断される可能性のある時期と、その方法を決定します。ハードウェア、I/O、UI、その他のスレッドでは異なります。スレッドが任意の場所で停止する可能性がある場合、これはリソース管理、トランザクションなどで未定義のプログラム動作を引き起こす可能性があります。

2
Alex F