C ++奇怪的多线程行为

时间:2015-10-31 06:35:29

标签: c++ multithreading

我正在通过带有DirectX 9的HLSL Pixel Shaders处理视频帧数据。我想通过命令缓冲区连续运行所有操作。一旦基本版本正常工作(逐个运行命令),则执行以下命令将返回先前生成的帧的结果,因为DirectX9存在1帧延迟,因此GPU在前一帧上工作我们正在填写当前的帧数据。

如果我用一个线程运行4个着色器,我得不到链中下一个着色器的帧,直到我得到第一个着色器的结果,所以获得正确输出的唯一方法是让DX9渲染一个虚拟场景,将性能降低一半。但是,当在8个线程上运行时,我应该能够拥有一个包含多达8个命令的命令缓冲区,这些命令可以一个接一个地链接。现在我还在逐个处理它们(还没有链接输出),而且它没有按我想要的方式工作。

有各种奇怪的多线程问题。

我在8个线程上运行4个着色器,这导致32个类调用AddCommandToChain。

首先,表现非常缓慢。如果为每个类实例启动ProcessFrames,那么性能要高得多;但后来我最终拥有32台占用大量内存的DirectX设备。理论上,最终版本应该提供比使用32个设备运行时性能更好的两倍。

其次,StartWorkerThread有时会被调用两次。似乎有一种我无法解决的竞争条件。

第三,有时候代码工作正常,有时它会死锁,有时候会随机冻结一段时间。

第四,我添加了一堆互斥锁并且它以这种方式工作,但如果我删除其中一些锁,那么我开始得到各种奇怪的行为。

如何“修复”此代码以使其以可接受的方式工作?

#include <list>
#include <thread>
#include <mutex>
#include <concurrent_queue.h>
using namespace concurrency;

static int threadCount;
static concurrent_queue<CommandStruct> cmdBuffer;
static std::mutex initLock, startLock, waiterLock;
static std::thread* WorkerThread;
static HANDLE WorkerWaiting;
static void AddCommandToQueue(CommandStruct* cmd, IScriptEnvironment* env);
static void StartWorkerThread(IScriptEnvironment* env);## Heading ##

int Shader::threadCount = 0;
concurrent_queue<CommandStruct> Shader::cmdBuffer;
std::mutex Shader::startLock, Shader::initLock, Shader::waiterLock;
std::once_flag OnceFlag;
std::thread* Shader::WorkerThread = NULL;
HANDLE Shader::WorkerWaiting = NULL;

void Shader::AddCommandToQueue(CommandStruct* cmd, IScriptEnvironment* env) {
    // Add command to queue.
    cmdBuffer.push(*cmd);

    // If thread is idle or not running, make it run.
    if (WorkerWaiting != NULL)
        SetEvent(WorkerWaiting);
    if (WorkerThread == NULL) {
        startLock.lock();
        if (WorkerThread == NULL)
            WorkerThread = new std::thread(StartWorkerThread, env);
        startLock.unlock();
    }
}

void Shader::StartWorkerThread(IScriptEnvironment* env) {
    if (WorkerThread != NULL)
        return;

    ProcessFrames Worker(env);

    // Start waiting event with a state meaning it's not waiting.
    WorkerWaiting = CreateEvent(NULL, TRUE, TRUE, NULL);

    // Process all commands in the queue.
    CommandStruct CurrentCmd, PreviousCmd;
    if (!cmdBuffer.try_pop(CurrentCmd))
        CurrentCmd.Path = NULL;
    while (CurrentCmd.Path != NULL) {
        // The result of the 1st execution will be returned on the 2nd call.
        if (FAILED(Worker.Execute(&CurrentCmd, NULL)))
            env->ThrowError("Shader: Failed to execute command");
        initLock.lock();
        SetEvent(CurrentCmd.Event); // Notify that processing is completed.
        initLock.unlock();

        PreviousCmd = CurrentCmd;
        if (!cmdBuffer.try_pop(CurrentCmd))
            CurrentCmd.Path = NULL;

        while (CurrentCmd.Path != NULL) {
            if (FAILED(Worker.Execute(&CurrentCmd, NULL)))
                env->ThrowError("Shader: Failed to execute command");
            initLock.lock();
            SetEvent(CurrentCmd.Event); // Notify that processing is completed.
            initLock.unlock();
            PreviousCmd = CurrentCmd;
            if (!cmdBuffer.try_pop(CurrentCmd))
                CurrentCmd.Path = NULL;
        }

        // Flush the device to get last frame.
        //Worker.Flush(&PreviousCmd);
        //SetEvent(PreviousCmd.WorkerEvent); // Notify that processing is completed.

        // When queue is empty, Wait for event to be set by AddCommandToQueue.
        waiterLock.lock();
        WaitForSingleObject(WorkerWaiting, 10000);
        ResetEvent(WorkerWaiting);

        // If there are still no commands after timeout, stop thread.
        if (!cmdBuffer.try_pop(CurrentCmd))
            CurrentCmd.Path = NULL;
        waiterLock.unlock();
    }

    // Release event and thread.
    startLock.lock();
    CloseHandle(WorkerWaiting);
    WorkerWaiting = NULL;
    WorkerThread = NULL;
    startLock.unlock();
}

编辑:即使我在调用SetEvent(WorkerWaiting)之前清楚地将命令添加到缓冲区,但在线程内,WaitForSingleObject(WorkerWaiting,10000)在缓冲区仍为空时被释放!并且线程反复退出。

这是Github上的完整代码文件 https://github.com/mysteryx93/AviSynthShader/blob/master/Src/WorkerThread.h

0 个答案:

没有答案