在Visual Studio调试器中程序卡住了。

8

一些背景信息:我正在尝试追踪一个导致我头疼的错误。在走了很多弯路之后(请参见this question),我最终得到了这段代码:

#include <thread>
#include <vector>
#include <iosfwd>
#include <sstream>
#include <string>
#include <windows.h>

int main()
{
    SRWLOCK srwl;
    InitializeSRWLock(&srwl);
    for(size_t i=0;i<1000;++i)
    {
        std::vector<std::thread>threads;
        for(size_t j=0;j<100;++j)
        {
            OutputDebugString(".");
            threads.emplace_back([&](){
                AcquireSRWLockExclusive(&srwl);
                //Code below modifies the probability to see the bug.
                std::this_thread::sleep_for(std::chrono::microseconds(1));
                std::wstringstream wss;
                wss<<std::this_thread::get_id();
                wss.str();
                //Code above modifies the probability to see the bug.
                ReleaseSRWLockExclusive(&srwl);});
        }
        for(auto&t:threads){t.join();}
        OutputDebugString((std::to_string(i)+"\n").data());
    }
    return 0;
}

当我在VS 2013调试器中运行此代码时,程序会挂起,并输出如下内容:
....................................................................................................0
....................................................................................................1
....................................................................................................2
...........................

奇怪的是,如果我暂停调试并检查正在发生的情况,其中一个线程会在AcquireSRWLockExclusive(在NtWaitForAlertByThreadId中)内部,显然程序挂起没有任何原因。当我点击继续时,程序就愉快地继续打印一些东西,直到再次被阻止。

你有任何想法吗?

更多信息:

  • 据我所知,这个错误只存在于Windows 8.1上。
  • 我尝试了VS2013.4和VS2015 RC。
  • 我在两台不同的计算机上都可以复现它,运行的是Windows 8.1。
  • 其中一台机器已经格式化,RAM、CPU和磁盘都经过了测试(一开始我只能在这台特定的机器上观察到该bug,因此我认为可能是由于故障引起的)
  • 我从未在Windows 7上重现过它。
  • 修改注释之间的代码以观察该bug可能很有用。当我添加微秒睡眠时,我最终可以在另一台计算机上复制该bug。
  • 使用VS2015 RC,我可以通过简单的std::mutex复现相同的行为。但在VS2013中,SRWLOCK似乎是观察该bug的必要条件。

可能你处于死锁状态,一旦进入调试器,它会改变同步方式,死锁就会消失。 - NathanOliver
@NathanOliver 这怎么可能?代码中只有一个互斥锁。 - Arnaud
程序在调试器外运行时是否会出现挂起的情况? - Retired Ninja
@RetiredNinja 它不会在调试器外挂起。 - Arnaud
难以直视。std::thread 是建立在并发时代之上的。既来之,则安之,使用 <ppl.h>中的Concurrency::reader_writer_lock。这可能是一个破绽,考虑向微软支持团队咨询。 - Hans Passant
@HansPassant 我向微软提交了一个错误报告。我也会研究一下你的ppl建议。 - Arnaud
2个回答

4

我尝试了热修复,它解决了问题。不过似乎变得更慢了。 - Arnaud

1

对我来说,这看起来像是Windows操作系统中的一个错误。在Win 8.1 / Server 2012R2 的2014年4月更新后,我使用了不同的代码变体,在调试器下使用新的Vista原语时会出现挂起。还有一些线程池等待函数也会挂起。看起来它主要与其他线程在等待/锁定时完成执行有关。这是一个简单的代码示例,在NtWaitForAlertByThreadId()函数下,它总是会在调试器下挂起:

#include <windows.h>

#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#pragma optimize("",off)
VOID CALLBACK _WorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID pUser, PTP_WORK Work)
{
    for (int i = 0; i < INT_MAX / 256; i++) {}
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    for (int i = 0; i < INT_MAX / 256; i++) {}
    return 0;
}
#pragma optimize("",on)

int _tmain(int argc, _TCHAR* argv[])
{
    LONGLONG c = 0;

    while(!_kbhit())
    {
        PTP_WORK ptpw = CreateThreadpoolWork(&_WorkCallback, NULL, NULL);
        if (ptpw != NULL) 
        {
            for(long i = 0; i < 3; i++) SubmitThreadpoolWork(ptpw);

            CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

            WaitForThreadpoolWorkCallbacks(ptpw, FALSE);
            CloseThreadpoolWork(ptpw);
        }

        printf("%I64d                                 \r", c++);
    }

    _getch();
    return 0;
}

很遗憾,我不知道应该向微软报告。

我也能在我的系统上重现这个卡顿。 - Arnaud

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接