为什么std :: condition_variable :: wait_for()在带VS2013的Windows上抛出一个不允许std :: system_error的操作?

时间:2015-12-06 17:30:25

标签: windows multithreading c++11 visual-c++ visual-studio-2013

我有一个简单的测试,在GUI程序上有后台线程,每秒一次将显示工作异步推回到GUI线程上。它在Windows上不起作用,std::condition_variable::wait_for()投放std::system_error类型operation not permitted.

这适用于Windows 7 x64上的Visual Studio 2013。它不适用于Linux上的gcc,因此-lpthread不计算在这里。关于这个问题的现有问题(Stack Overflow和其他地方)都是针对gcc / Linux / -lpthread的。事实上,我在Linux上有一个等效的测试(使用GTK +),它运行得很好。

这是一个例子。它会打开一个带有按钮和多行编辑框的窗口。每隔一秒,您应该会看到添加到编辑框中的行One second passed。单击该按钮会立即添加行Saying something。线条永远不应混合。

相反,如果你运行这个程序,你应该看到

class std::system_error caught: operation not permitted: operation not permitted

在命令行上,One second passed消息永远不会显示。

我正在建设

cl /TP wincondvartest.cpp /W4 /Zi /EHsc /link /incremental:no user32.lib kernel32.lib gdi32.lib

发生了什么事?感谢。

// 6 december 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
// get Windows version right; right now Windows Vista
#define WINVER 0x0600               /* according to Microsoft's winnls.h */
#define _WIN32_WINNT 0x0600     /* according to Microsoft's sdkddkver.h */
#define _WIN32_WINDOWS 0x0600       /* according to Microsoft's pdh.h */
#define _WIN32_IE 0x0700            /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x06000000    /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <string.h>
#include <stdlib.h>
#include <typeinfo>
#include <time.h>

HWND mainwin;
std::condition_variable cv;
std::mutex m;
std::unique_lock<std::mutex> ourlock(m);
std::thread *timeThread;

bool wait(void)
try {
    return cv.wait_for(ourlock, std::chrono::seconds(1)) == std::cv_status::timeout;
} catch (const std::exception &e) {
    fprintf(stderr, "%s caught: %s\n", typeid (e).name(), e.what());
    return false;       // kill the thread
}

void threadproc(void)
{
    while (wait())
        PostMessageW(mainwin, WM_APP, 0, 0);
}

HWND edit;

void appendline(const WCHAR *wc)
{
    LRESULT n;

    n = SendMessageW(edit, WM_GETTEXTLENGTH, 0, 0);
    SendMessageW(edit, EM_SETSEL, n, n);
    SendMessageW(edit, EM_REPLACESEL, FALSE, (LPARAM) wc);
}

HWND button;

LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case WM_COMMAND:
        if (lParam == (LPARAM) button)
            appendline(L"Saying something\n");
        break;
    case WM_APP:
        appendline(L"One second passed\n");
        break;
    case WM_CLOSE:
        cv.notify_all();
        timeThread->join();
        PostQuitMessage(0);
        break;
    }
    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

int main(void)
{
    WNDCLASSW wc;
    RECT r;
    MSG msg;

    ZeroMemory(&wc, sizeof (WNDCLASSW));
    wc.lpszClassName = L"mainwin";
    wc.lpfnWndProc = wndproc;
    wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
    RegisterClassW(&wc);

    r.left = 0;
    r.top = 0;
    r.right = 10 + 300 + 10;
    r.bottom = 10 + 20 + 5 + 195 + 10;
    AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW, FALSE, 0);
    mainwin = CreateWindowExW(0,
        L"mainwin", L"mainwin",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        r.right - r.left, r.bottom - r.top,
        NULL, NULL, NULL, NULL);

    button = CreateWindowExW(0,
        L"button", L"Say Something",
        BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
        10, 10,
        300, 20,
        mainwin, NULL, NULL, NULL);

    edit = CreateWindowExW(WS_EX_CLIENTEDGE,
        L"edit", L"",
        ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_WANTRETURN | WS_CHILD | WS_VISIBLE | WS_VSCROLL,
        10, 10 + 20 + 5,
        300, 195,
        mainwin, NULL, NULL, NULL);

    timeThread = new std::thread(threadproc);

    ShowWindow(mainwin, SW_SHOWDEFAULT);
    UpdateWindow(mainwin);

    while (GetMessageW(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    return 0;
}

1 个答案:

答案 0 :(得分:3)

您的程序通过尝试解锁由其他线程锁定的互斥锁来展示未定义的行为。 ourlock最初是在主线程上构造的,是一个全局变量,此时它会锁定m。然后在第二个线程上将其传递给cv.wait_for。这是明确禁止的:

  

<强> [thread.condition.condvar]

template <class Rep, class Period>
    cv_status wait_for(unique_lock<mutex>& lock,
                       const chrono::duration<Rep, Period>& rel_time);
     

25 要求: lock.owns_lock()为真且lock.mutex() 被调用线程锁定 ... < / p>

强调我的。