使用 MSVC 编译的多线程应用程序在运行时失败
A multithreading app compiled with MSVC fails at runtime
我已经实现了一个 class 循环运行提供的函数。
//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) 时,程序也会编译,但是它在运行时失败。
当我使用发布配置时,我从线程之一得到以下异常:
Unhandled exception at 0x000007F8D8E14A30 (msvcr120.dll) in Program.exe: Fatal program exit requested.
当我使用调试配置时,每秒都会弹出一个 Microsoft Visual C++ Runtime Library 错误:
Debug Error!
Program: ...\path\Program.exe
R6010
- abort() has been called
在两种配置中:
异常是 thrown/the 错误在计时器循环的第二次迭代中开始弹出。
即使 thread(handler)
被调用,程序也不会进入 Tick
函数一次。
虽然错误发生时的堆栈跟踪在这两种配置中有所不同,但它们都不包含我的代码中的任何内容。都以 ntdll.dll!UserThreadStart()
开头;调试版本以 msvcr123d.dll!_NMSG_WRITE()
结尾,发布版本以 msvcr120.dll!abort()
.
结尾
为什么会出现问题,为什么只有在使用MSVC编译应用程序时才会出现问题?它是某种 MSVC 的错误吗?或者也许我应该更改编译器配置中的某些内容?
您的线程正在抛出 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 调用之后被破坏。不知道这是为什么。
我已经实现了一个 class 循环运行提供的函数。
//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) 时,程序也会编译,但是它在运行时失败。
当我使用发布配置时,我从线程之一得到以下异常:
Unhandled exception at 0x000007F8D8E14A30 (msvcr120.dll) in Program.exe: Fatal program exit requested.
当我使用调试配置时,每秒都会弹出一个 Microsoft Visual C++ Runtime Library 错误:
Debug Error!
Program: ...\path\Program.exe
R6010 - abort() has been called
在两种配置中:
异常是 thrown/the 错误在计时器循环的第二次迭代中开始弹出。
即使
thread(handler)
被调用,程序也不会进入Tick
函数一次。虽然错误发生时的堆栈跟踪在这两种配置中有所不同,但它们都不包含我的代码中的任何内容。都以
ntdll.dll!UserThreadStart()
开头;调试版本以msvcr123d.dll!_NMSG_WRITE()
结尾,发布版本以msvcr120.dll!abort()
. 结尾
为什么会出现问题,为什么只有在使用MSVC编译应用程序时才会出现问题?它是某种 MSVC 的错误吗?或者也许我应该更改编译器配置中的某些内容?
您的线程正在抛出 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 调用之后被破坏。不知道这是为什么。