使用 MSVC 编译的多线程应用程序在运行时失败
Posted
技术标签:
【中文标题】使用 MSVC 编译的多线程应用程序在运行时失败【英文标题】:A multithreading app compiled with MSVC fails at runtime 【发布时间】:2015-02-25 11:51:10 【问题描述】:我已经实现了一个循环运行提供的函数的类。
//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) 时,程序也可以编译,但在运行时会失败。
当我使用发布配置时,我从其中一个线程中得到以下异常:
Program.exe 中 0x000007F8D8E14A30 (msvcr120.dll) 处的未处理异常:请求致命程序退出。
当我使用调试配置时,每秒都会弹出一个 Microsoft Visual C++ Runtime Library 错误:
调试错误!
程序:...\path\Program.exe
R6010 - abort() 已被调用
在两种配置中:
在计时器循环的第二次迭代中抛出异常/错误开始弹出。
即使调用了thread(handler)
,程序也不会进入Tick
函数。
虽然这两种配置中出错时的堆栈跟踪不同,但它们都不包含我的代码中的任何内容。两者都以ntdll.dll!UserThreadStart()
开头;调试以msvcr123d.dll!_NMSG_WRITE()
结束,发布以msvcr120.dll!abort()
结束。
为什么会出现问题?为什么只有在使用 MSVC 编译应用程序时才会出现这些问题?它是某种 MSVC 的错误吗?或者我应该改变编译器的配置吗?
【问题讨论】:
你需要睡一觉,你应该加入 Stop()。这只是一次迭代! 我不明白你在说什么。有一个无限循环正在进行。在每次迭代中,都会产生一个新线程。我应该加入哪个线程,为什么? 当你使用 stop(); “无限循环”结束。你可以等待加入他:) 我知道这一点,但这不是我写这门课时的意图。Stop()
应该阻止计时器进行新的调用,而不是停止计时器启动的所有活动。此外,如果我想等待调用结束,我应该累积对所有调用线程的引用,因为之前调用的线程可能仍在工作。
请不要手动调用lock()
和unlock()
,而是使用std::lock_guard
。现在,如果有任何东西在你的锁中引发异常,你就会死锁。
【参考方案1】:
您的线程正在抛出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 调用后被破坏。不知道为什么会这样。
【讨论】:
确实,这解决了问题。我认为有一些隐式转换,其中指向函数的指针被转换为function
对象。然后该对象在 ctor 中使用并在之后销毁,因为不再需要它。但是为什么这段代码用 g++ 编译能正常工作呢?
一定是函数对象还在g++的作用域内。虽然我认为如果一个临时对象绑定到一个引用,那么它的生命周期应该延长吗?
It seems 在这种情况下编译器的行为是未定义的。所以我很幸运 g++ 没有在 function
的地方放任何东西。
这可能只是次差的普通未定义行为——它似乎工作正常。暂时的,尽管不再在范围内,只是还没有被覆盖。如果某个其他值在内存中重用该位置,并且将“用户”提供的值写入其中,则存在安全漏洞。
@tearvisus 不,你很不幸,它没有在你负担不起的情况下严重破坏某些东西之前指出你的错误。另见“快速失败”。以上是关于使用 MSVC 编译的多线程应用程序在运行时失败的主要内容,如果未能解决你的问题,请参考以下文章