4

提供された関数を周期的に実行するクラスを実装しました。

//Timer.h
#include <chrono>
#include <mutex>
#include <thread>

class Timer {
public:
    Timer(const std::chrono::milliseconds period, const std::function<void()>& handler);
    ~Timer();
    void Start();
    void Stop();
    bool IsRunning() const;

private:
    const std::function<void()>& handler;
    const std::chrono::milliseconds period;
    bool isRunning = false;
    mutable std::recursive_mutex lock;
    int counter = 0;

    void DoLoop(int id);
};

//Timer.cpp
#include "Timer.h"

Timer::Timer(const std::chrono::milliseconds period, const std::function<void()>& handler) :handler(handler), period(period), lock(){}

Timer::~Timer() {
    Stop();
}

void Timer::Stop() {
    lock.lock();
    isRunning = false;  
    lock.unlock();
}

void Timer::Start() {
    lock.lock();
    if (!isRunning) {
        isRunning = true;
        counter++;
        std::thread(&Timer::DoLoop, this, counter).detach();
    }
    lock.unlock();
}

void Timer::DoLoop(int id) {
    while (true){
        std::this_thread::sleep_for(period);
        lock.lock();
        bool goOn = isRunning && counter==id;
        if (goOn) std::thread(handler).detach();
        lock.unlock();

        if (!goOn)
            break;
    }
}

bool Timer::IsRunning() const {
    lock.lock();
    bool isRunning = this->isRunning;
    lock.unlock();
    return isRunning;
}

そして、これが機能するかどうかを確認するための簡単なプログラムです。

void Tick(){ cout << "TICK" << endl; }

int main() {
    Timer timer(milliseconds(1000), Tick);
    timer.Start();
    cin.get();
}

g++ でアプリをビルドすると、プログラムは問題なくビルドおよび実行されます。ただし、Microsoft のコンパイラ (v18) を使用すると、プログラムもコンパイルされますが、実行時に失敗します。

リリース構成を使用すると、スレッドの 1 つから次の例外が発生します。

Program.exe の 0x000007F8D8E14A30 (msvcr120.dll) で未処理の例外: 致命的なプログラムの終了が要求されました。

デバッグ構成を使用すると、Microsoft Visual C++ ランタイム ライブラリ エラーが毎秒表示されます。

デバッグ エラー!

プログラム: ...\path\Program.exe

R6010 - abort() が呼び出されました

両方の構成で:

  • タイマーのループの 2 回目の反復で、例外がスローされるか、エラーが発生し始めます。

  • 呼び出されても、プログラムはTick関数に一度も入りません。thread(handler)

  • エラー発生時のスタック トレースはこれら 2 つの構成で異なりますが、どちらにも私のコードからの情報は含まれていません。ntdll.dll!UserThreadStart()どちらも;で始まります。デバッグは で終わりmsvcr123d.dll!_NMSG_WRITE()、リリースは で終わりmsvcr120.dll!abort()ます。

問題が発生するのはなぜですか?また、アプリが MSVC でコンパイルされている場合にのみ発生するのはなぜですか? それはある種のMSVCのバグですか?それとも、コンパイラの構成で何かを変更する必要がありますか?

4

1 に答える 1

3

スレッドがスローされています。std::bad_function_call例外が処理されないため、ランタイムが呼び出していabort()ます。

変化:

const std::function<void()>& handler;

const std::function<void()> handler;

問題を修正します。これは、スレッド間で共有しているためだと思いますか?

ローカルを作成し、その参照を渡す場合にも機能します。

  const std::function<void()> f = Tick;
  Timer timer(std::chrono::milliseconds(1000), f);

したがって、何らかの形で範囲外になったに違いありません。

編集:実際、関数オブジェクトは ctor 呼び出しの後に破棄されます。これがなぜなのかわかりません。

于 2015-02-25T13:31:48.907 に答える