C# 改进多线程代码设计以防止争用情况

C# 改进多线程代码设计以防止争用情况,c#,c++,multithreading,winapi,race-condition,C#,C++,Multithreading,Winapi,Race Condition,我遇到了一个问题,我不确定是否可以用我想要的方式解决它。我对比赛条件有问题 我有一个项目作为C++ DLL(主引擎)运行。 然后我有第二个C#进程,它使用C++/CLI与主引擎(编辑器)通信 编辑器将引擎窗口作为子窗口托管。其结果是子窗口异步接收输入消息(请参见RiProcessMouseMessage())。通常只有在调用window->PollEvents()时才会发生这种情况 因此,您可以看到,当在主引擎循环中调用RiProcessMouseMessage()而调用Reset()时,竞争条

我遇到了一个问题,我不确定是否可以用我想要的方式解决它。我对比赛条件有问题

我有一个项目作为C++ DLL(主引擎)运行。 然后我有第二个C#进程,它使用C++/CLI与主引擎(编辑器)通信

编辑器将引擎窗口作为子窗口托管。其结果是子窗口异步接收输入消息(请参见
RiProcessMouseMessage()
)。通常只有在调用
window->PollEvents()时才会发生这种情况

因此,您可以看到,当在主引擎循环中调用
RiProcessMouseMessage()
而调用
Reset()
时,竞争条件发生。如果不清楚:需要使用
Reset()
函数将状态重置回其帧的默认数据,以便每帧都能正确读取数据


现在我非常清楚,我可以通过在gatherState更新周围添加一个
mutex
来轻松解决这个问题,但如果可能的话,我希望避免这种情况。基本上,我是在问,有没有可能重新设计此代码,使其无锁?

您是在问无锁,如果两端都改变了缓冲区,这是不太可能的。但若你们要求锁是优化的,几乎是瞬时的,那个么你们可以使用FIFO逻辑。您可以使用.net的ConcurrentQueue“https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1?view=net-5.0“从该队列写入更新和轮询更新

如果你真的摆脱了锁,那么你可以检查无锁循环数组,也称为无锁环缓冲, 如果您想深入了解硬件级别以了解其背后的逻辑,那么您可以进行检查,以便了解底层的并发性;受限制,当一端仅在已知间隔/边界内写入,而另一端仅在已知间隔/边界内读取时,无锁环形缓冲区可以工作。可以检查所问的类似问题:


Boost有著名的无锁实现:

Hmm,,是的,队列实际上可以在这里工作。我实际上需要它在C++中,但是我已经有一个并发队列实现。我明天就试一试。谢谢你的建议,有时候我只是需要一双眼睛来看待问题。
main engine loop {
    RiProcessMouseMessage(); // <- Called by the default windows message poll function from the child window
    
    foreach(inputDevice)
        inputDevice->UpdateState();

    otherCode->UseCurrentInput();
}
main editor loop {
    RiProcessMouseMessage(); // <- This one is called by the editor (parent) window, but is using the message loop of the (child) engine window
}
void Win32RawInput::RiProcessMouseMessage(const RAWMOUSE& rmouse, HWND hWnd) {
    MouseState& state = Input::mouse._GetGatherState();

    // Check Mouse Position Relative Motion
    if (rmouse.usFlags == MOUSE_MOVE_RELATIVE) {
        vec2f delta((float)rmouse.lLastX, (float)rmouse.lLastY);
        delta *= MOUSE_SCALE;
        state.movement += delta;

        POINT p;
        GetCursorPos(&p);
        state.cursorPosGlobal = vec2i(p.x, p.y);

        ScreenToClient(hWnd, &p);
        state.cursorPos = vec2i(p.x, p.y);
    }

    // Check Mouse Wheel Relative Motion
    if (rmouse.usButtonFlags & RI_MOUSE_WHEEL)
        state.scrollMovement.y += ((float)(short)rmouse.usButtonData) / WHEEL_DELTA;

    if (rmouse.usButtonFlags & RI_MOUSE_HWHEEL)
        state.scrollMovement.x += ((float)(short)rmouse.usButtonData) / WHEEL_DELTA;

    // Store Mouse Button States
    for (int i = 0; i < 5; i++) {
        if (rmouse.usButtonFlags & maskDown_[i]) {
            state.mouseButtonState[i].pressed = true;
            state.mouseButtonState[i].changedThisFrame = true;
        } else if (rmouse.usButtonFlags & maskUp_[i]) {
            state.mouseButtonState[i].pressed = false;
            state.mouseButtonState[i].changedThisFrame = true;
        }
    }
}
void UpdateState() {
    currentState = gatherState; // Copy gather state to current
    Reset(gatherState);         // Reset the old buffer so the next time the buffer it's used it's all good
    
   // Use current state to check stuff
   // For the rest of this frame currentState should be used
}

MouseState& _GetGatherState() { return gatherState; }

void Reset(MouseState& state) { // Might need a lock around gatherState :(
    state.movement = vec2f::zero;
    state.scrollMovement = vec2f::zero;

    for (int i = 0; i < 5; ++i)
        state.mouseButtonState[i].changedThisFrame = false;
}