【问题标题】:Why is my while loop not working when I do nothing inside of it (do-nothing loop)?为什么当我在其中什么都不做(不做任何循环)时,我的 while 循环不起作用?
【发布时间】:2020-11-25 14:33:10
【问题描述】:

我只是尝试使用 while 循环来帮助控制我的程序的每秒帧数,该程序显示实时摄像头馈送以及与摄像头正在查看的内容相对应的数据。这不是微不足道的原因是因为调用getFramesAfterCapture() 的循环运行速度比相机获取图像的速度要快得多(因为它是在一个更新方法中调用的,该方法收集当前帧和都需要显示的数据)。

我的想法是让这个函数像使用互斥锁一样简单地等待,直到我的 while 循环中的条件不再满足。回调在另一个线程中运行,captureComplete 是该类的静态成员变量,因此它是它的唯一实例。当从我的相机捕获帧的回调完成捕获新图像时,此 bool 的值会发生变化。必须以这种方式完成,因为我需要将显示到屏幕的数据与捕获的帧同步,这样数据和视频显示匹配。

这是我的代码:

    long CALLBACK FrameGrabber::destCallback(LPVOID lpUser, LPVOID lpReserved) {
        // copy the data into the bitmap
        if (WaitForSingleObject(hMutex, 10) == WAIT_OBJECT_0)
        {
            DWORD dwSize = VIDEO_SIZE;
            //get bitmap
            dpGetSurfaceData(hDest, bitmap + sizeof(BITMAPINFOHEADER), &dwSize);
    
            ReleaseMutex(hMutex);
            // set capture to complete so that waitForCapture releases from the do-nothing loop
            WaitForSingleObject(captureCmpltMutex, INFINITE);
            captureComplete = true;
            ReleaseMutex(captureCmpltMutex);
        }
        
        return 0;
    }
    
    BYTE* FrameGrabber::getFrameAfterCapture() {
        // loop while capture is still not completed
        ////while (!captureComplete);
        while (!captureComplete) { 
            std::cout << "Waiting for capture to complete..." << std::endl;
        }
    
        //std::cout << "Capture Complete!" << std::endl;
        // when capture is completed set captureComplete back to false so that this waits again until the next frame is acquired
        WaitForSingleObject(captureCmpltMutex, INFINITE);
        captureComplete = false;
        ReleaseMutex(captureCmpltMutex);
    
        return bitmap;
    }

我的循环工作正常...

    while (!captureComplete) { 
        std::cout << "Waiting for capture to complete..." << std::endl;
    }

但是当我把它改成这个时不起作用......

    while (!captureComplete);

或者这个……

    while (!captureComplete) { }

或者这个……

    while (!captureComplete) continue;

或者这个……

    while (!captureComplete){ continue; }

我更喜欢任何其他选项,因为我宁愿在程序完成后不在程序中保留打印语句。我希望我的代码看起来像其他似乎不起作用的选项一样干净整洁。

如果有另一个更优雅的选择,我愿意改变它,我只需要一个有效且看起来干净的解决方案。

【问题讨论】:

  • 任何体面的编译器都会看到它是一个无操作循环并优化它,因为它永远不需要执行。 (不执行任何操作的代码如果被删除,则对代码没有影响,因为……好吧,它是什么也不执行的代码。)
  • 即使它没有被编译出来,你可能也不希望那里有一个消耗 CPU 的繁忙自旋循环,所以我将 print 语句替换为短时睡眠以释放 CPU做一些有用的事情。
  • 睡眠是我正在考虑的事情,但我不知道这个项目的未来可能会怎样,而且我不想睡觉,不管它有多快成为任何瓶颈的原因。我在下面使用信号量提到了我的解决方案。
  • 您的代码包含数据竞争:一个线程修改captureComplete,而另一个线程正在读取它。这在 C++ 中是不合法的。 C++ 要求对共享变量的多线程访问受同步保护或声明为atomic

标签: c++ winapi while-loop mutex static-members


【解决方案1】:

我能够使用二进制信号量而不是互斥锁或静态成员布尔值找出更优雅的解决方案。使用互斥锁和二进制信号量的区别在于,二进制信号量不需要我在 getFrameCapture() 结束时释放信号量。基本上,当一个信号量被释放时,它所持有的计数会增加一。相反,当等待函数返回时,信号量持有的计数减一。

这允许信号行为,如果我在开始时等待互斥锁并在 getFrameCapture() 方法返回等待捕获帧的函数的整个点之前释放它,则会丢失。同样,如果我在函数返回之前不释放它,互斥锁将返回 WAIT_ABANDONED 告诉程序员线程意外退出并且互斥锁已被它放弃。

溶胶:

long CALLBACK FrameGrabber::destCallback(LPVOID lpUser, LPVOID lpReserved) {
    // copy the data into the bitmap
    if (WaitForSingleObject(hMutex, 10) == WAIT_OBJECT_0)
    {
        DWORD dwSize = VIDEO_SIZE;
        //get bitmap
        dpGetSurfaceData(hDest, bitmap + sizeof(BITMAPINFOHEADER), &dwSize);

        ReleaseMutex(hMutex);
        //Release semaphore (emit signal)
        ReleaseSemaphore(captureCmpltSemaphore, 1, NULL);
    }
    
    return 0;
}

BYTE* FrameGrabber::getFrameAfterCapture() {
    //Wait for semaphore to be released (Signaled)
    WaitForSingleObject(captureCmpltSemaphore, INFINITE);
    //return bitmap
    return bitmap;
}

我参考了这个链接来找到我的解决方案: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-releasesemaphore

【讨论】:

  • 从 C++11 开始,您还可以使用 threading support library 中的同步工具。这些将为您提供更好的可移植性,这对于此代码似乎不是问题,因为它是贯穿始终的 Windows 代码。
  • 值得一提。我不喜欢编写特定于操作系统的代码,但这正是我的组织以前在项目中使用的。在此处将项目的其余部分迁移到使用非操作系统特定的同步可能是有益的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
  • 2012-11-07
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2022-12-12
相关资源
最近更新 更多