条件变量WinAPI不起作用。为什么?

时间:2017-03-28 19:16:27

标签: windows multithreading winapi visual-c++

我想同步自动生成的线程。我写了这样的代码。此代码定期按生成的顺序写入每个线程的名称。

但是当我运行我的代码时,有时候我会得到一个很好的结果,有时候我会遇到这样的错误:所有都挂起(用清晰的黑屏睡觉)。它意外发生。

通过调试,我了解到有时候子线程并不想唤醒。谁能说出来:为什么?我该如何解决这个问题?

我在Windows 10 x64上使用Microsoft Visual Studio 2010 SP1 Professional 我的代码:

的main.cpp

if (csrf_token mismatch) {
    return redirect()->back();
}

ThreadFuncs.cpp

#include "ThreadManager.h"
#include "ThreadFuncs.h"

int main() {
    ThreadManager mng(2.0, 1.3);
    mng.runAll();

    Sleep(100000);

    return 0;
}

ThreadManager.h

#include <iostream>
#include <string>

#include "Sync.h"
#include "ThreadManager.h"

#include "ThreadFuncs.h"

DWORD WINAPI threadPrinter(LPVOID ptr)
{
    ThreadManager* manager = reinterpret_cast<ThreadManager*>(ptr);
    if (!manager)
    {
        return 1;
    }

    CRITICAL_SECTION cs;
    InitializeCriticalSection(&cs);
    EnterCriticalSection(&cs);

    while (!manager->isStopPrinting)
    {
        EnterCriticalSection(&manager->workWithFlags);

        for (auto it = manager->flags.begin(); it != manager->flags.end(); ++it)
        {
            (*it)->operation = OPERATION_START_WRITING_NAME;
            WakeConditionVariable(&(*it)->canWork);

            SleepConditionVariableCS(&(*it)->isEndWork, &cs, INFINITE);
        }

        LeaveCriticalSection(&manager->workWithFlags);

        Sleep(manager->showInterval * 1000);
    }

    LeaveCriticalSection(&cs);
    DeleteCriticalSection(&cs);

    return 0;
}

DWORD WINAPI threadGenerator(LPVOID ptr)
{
    ThreadManager* manager = reinterpret_cast<ThreadManager*>(ptr);
    if (!manager)
    {
        return 1;
    }

    while (!manager->isStopGeneration)
    {
        manager->generateNewThread();

        Sleep(manager->createNewThreadInterval * 1000);
    }

    return 0;
}

DWORD WINAPI threadChild(LPVOID ptr)
{
    Sync* s = reinterpret_cast<Sync*> (ptr);
    if (s == NULL)
    {
        return 1;
    }

    std::string name = "Th_" + std::to_string(static_cast<long long>(s->index));

    CRITICAL_SECTION cs;
    InitializeCriticalSection(&cs);
    EnterCriticalSection(&cs);

    while (true)
    {       
        SleepConditionVariableCS(&s->canWork, &cs, INFINITE);

        if (s->operation == OPERATION_EXIT_THREAD)
        {
            break;
        }

        if (s->operation == OPERATION_START_WRITING_NAME)
        {
            std::cout << name << " ";
            WakeConditionVariable(&s->isEndWork);
            continue;
        }

        WakeConditionVariable(&s->isEndWork);
    }

    LeaveCriticalSection(&cs);
    DeleteCriticalSection(&cs);

    return 0;
}

ThreadManager.cpp

#pragma once

#include <windows.h>
#include <deque>
#include "Sync.h"
#include "ThreadFuncs.h"

class ThreadManager
{
    std::deque <Sync *> flags;
    CRITICAL_SECTION workWithFlags;

    friend DWORD WINAPI threadGenerator(LPVOID);
    friend DWORD WINAPI threadPrinter(LPVOID);

    HANDLE printerThread;
    HANDLE generatorThread;

    const double showInterval;
    const double createNewThreadInterval;
    bool isStopGeneration;
    bool isStopPrinting;    

public:
    ThreadManager(const double& showInterval, const double& createNewThreadInterval);
    ~ThreadManager();

    void generateNewThread();
    bool removeThread();            //kill random thread
    bool removeThread(int index);

    void runAll();
    void stopAll();

    int getNumOfThreads() const;
};

Sync.h

#include "ThreadManager.h"


ThreadManager::ThreadManager(const double& showInterval, const double& createNewThreadInterval) :
    showInterval(showInterval), createNewThreadInterval(createNewThreadInterval), isStopGeneration(true), isStopPrinting(true)
{

    InitializeCriticalSection(&workWithFlags);

    printerThread = NULL;
    generatorThread = NULL;

}

int ThreadManager::getNumOfThreads() const
{
    return flags.size();
}

void ThreadManager::runAll()
{
    //run generation
    isStopGeneration = false;

    if (generatorThread != NULL)
    {
        return;
    }

    generatorThread = CreateThread(NULL, 0, threadGenerator, this, /*run immediately*/ 0, NULL);
    if (generatorThread == NULL)
    {
        //Stream::log("Error creating generation thread");
        isStopGeneration = true;
    }

    //run printing
    isStopPrinting = false;

    if (printerThread != NULL)
    {
        return;
    }

    printerThread = CreateThread(NULL, 0, threadPrinter, this, /*run immediately*/ 0, NULL);
    if (printerThread == NULL)
    {
        //Stream::log("Error creating printer thread");
        isStopPrinting = true;
    }
}

void ThreadManager::stopAll()
{
    //stop generation
    if (!generatorThread)
    {
        return;
    }

    isStopGeneration = true;

    WaitForSingleObject(generatorThread, INFINITE);

    CloseHandle(generatorThread);

    generatorThread = NULL;

    //stop printing
    if (!printerThread)
    {
        return;
    }

    isStopPrinting = true;

    WaitForSingleObject(printerThread, INFINITE);

    CloseHandle(printerThread);

    printerThread = NULL;
}

ThreadManager::~ThreadManager()
{
    stopAll();

    while (!flags.empty())
    {
        removeThread(flags.size() - 1);
    }

    DeleteCriticalSection(&workWithFlags);
}

void ThreadManager::generateNewThread()
{
    static int newIndex = 0;
    Sync* s = new Sync();

    s->index = newIndex;
    newIndex++;

    s->threadHandle = CreateThread(NULL, 0, threadChild, s, /*run immediately*/ 0, NULL);
    if (s->threadHandle == NULL)
    {
        //Stream::log("Error creating new thread");
        delete s;
        return;
    }

    EnterCriticalSection(&workWithFlags);
    flags.push_back(s);
    LeaveCriticalSection(&workWithFlags);
}

bool ThreadManager::removeThread(int index)
{
    EnterCriticalSection(&workWithFlags);

    if (flags.size() <= index || index < 0)
    {
        LeaveCriticalSection(&workWithFlags);
        return false;
    }

    Sync* s = flags[index];
    flags.erase(flags.begin() + index);

    s->operation = OPERATION_EXIT_THREAD;
    WakeConditionVariable(&s->canWork);

    WaitForSingleObject(s->threadHandle, INFINITE);

    delete s;

    LeaveCriticalSection(&workWithFlags);

    return true;
}

bool ThreadManager::removeThread()
{
    EnterCriticalSection(&workWithFlags);
    int size = flags.size();
    LeaveCriticalSection(&workWithFlags);

    if (!size)
    {
        return false;
    }

    return removeThread(rand() % size);
}

更新:

堆栈跟踪(我理解): Stack trace

1 个答案:

答案 0 :(得分:1)

您的threadPrinter()threadChild()函数未正确同步线程。您正在创建一个无用的新本地CRITICAL_SECTION,因为其他线程无权访问它,因此它实际上并未锁定对任何内容的访问权限。当您应该通过CRITICAL_SECTION时,您将本地SleepConditionVariableCS()传递给manager->workWithFlags。但这样做会引入一个新的逻辑漏洞,因为一旦一个线程解锁workWithFlags以等待一个条件变量,其他线程就可以自由修改flags的内容,你在等待的时候没有检查当发出条件变量信号时,线程重新获取workWithFlags锁。

根据您的尝试,您可能根本不应该使用条件变量。条件变量用于在等待变量发出信号时解锁获取的锁(临界区或超薄读取器/写入器锁),然后在发出信号后再次重新获取该锁。这不是您的代码所需要的。使用可签名事件会更有意义。查看CreateEvent()SetEvent() / ResetEvent()WaitForSingleObject()。发出事件信号以开始某些工作,并在完成该工作时发出另一个事件的信号。您不需要为此使用条件变量。