C++ 使用MSVC编译的多线程应用程序在运行时失败

C++ 使用MSVC编译的多线程应用程序在运行时失败,c++,c++11,visual-c++,gcc,cl,C++,C++11,Visual C++,Gcc,Cl,我实现了一个类,它循环运行提供的函数 //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

我实现了一个类,它循环运行提供的函数

//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;
}
//Timer.h
#包括
#包括
#包括
班级计时器{
公众:
计时器(const std::chrono::毫秒周期,const std::函数和处理程序);
~Timer();
void Start();
无效停止();
bool IsRunning()常量;
私人:
const std::函数和处理程序;
常数std::chrono::毫秒周期;
bool isRunning=false;
可变std::递归互斥锁;
int计数器=0;
void DoLoop(int-id);
};
//Timer.cpp
#包括“Timer.h”
计时器::计时器(const std::chrono::毫秒周期,const std::函数和处理程序):处理程序(处理程序),周期(周期),锁(){}
计时器::~Timer(){
停止();
}
void Timer::Stop(){
lock.lock();
isRunning=false;
lock.unlock();
}
void Timer::Start(){
lock.lock();
如果(!正在运行){
isRunning=true;
计数器++;
std::thread(&Timer::DoLoop,this,counter).detach();
}
lock.unlock();
}
无效计时器::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();
如果(!傻瓜)
打破
}
}
布尔计时器::IsRunning()常量{
lock.lock();
bool isRunning=此->isRunning;
lock.unlock();
返回正在运行;
}
下面是一个简单的程序,看看它是否有效:

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

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

void Tick(){cout您的线程正在抛出
std::bad_function_call
,异常未被处理,因此运行时正在调用
abort()

更改:

const std::function<void()>& handler;
const std::函数和处理程序;

const std::函数处理程序;
修复了问题。我想这是因为你在线程之间共享它

如果创建一个本地文件并传递对该文件的引用,也可以使用该文件:

  const std::function<void()> f = Tick;
  Timer timer(std::chrono::milliseconds(1000), f);
const std::函数f=Tick;
定时器(标准:时钟:毫秒(1000),f);
所以它一定是超出了范围


编辑:事实上,函数对象在调用ctor后会被销毁。不确定原因。

您需要在main中进行一次睡眠,并且应该执行join in Stop()。这只是一次迭代!我不明白你在说什么。有一个无限循环在进行。在每次迭代中都会产生一个新线程。我应该加入哪个线程,为什么?当你使用stop()时,“无限循环”结束。你可以等待加入他:)我知道这一点,但这不是我在编写这个类时的意图。
stop()
应该阻止计时器进行新调用,而不是停止计时器启动的所有活动。此外,如果我想等待调用结束,我应该累积对所有调用线程的引用,因为以前调用的线程可能仍在工作。请不要调用
lock()
unlock()
手动,使用一个。现在,如果锁中出现异常,你会死锁。事实上,这解决了问题。我认为有一种隐式转换,指向函数的指针被转换为
函数
对象。然后该对象被用在ctor中,然后被销毁,因为它是not不再需要了。但是为什么这段代码在用g++编译时工作得很好?这一定是因为函数对象仍然在g++中的作用域上。虽然我认为如果临时对象绑定到引用,那么它的生命期应该延长?编译器在这种情况下的行为是未定义的。所以我很幸运g++没有放入任何内容
函数
的位置。它可能只是一种普通的未定义的行为,仅次于最坏的一种-它似乎工作正常。临时的,尽管不再在范围内,只是还没有被覆盖。如果其他一些值重用内存中的该位置,并将“用户”提供的值写入其中,那么您有一个安全漏洞。@tearvisus不,您很不幸,它没有在您最无力承担的时候严重破坏某些东西之前指出您的错误。另请参阅“fail fast”。
  const std::function<void()> f = Tick;
  Timer timer(std::chrono::milliseconds(1000), f);