【问题标题】:How to prevent thread starvation如何防止线程饥饿
【发布时间】:2016-10-31 04:35:55
【问题描述】:

我正在编写一个零延迟的云游戏服务器。这是一个软件管道。在第一阶段,我们捕获屏幕,在第二阶段,我们将其编码为视频。

但是,经过一段时间后,第二阶段会冻结。我尝试了许多独立于平台的方法,但是它们中的任何一个最终都会冻结。 How to prevent threads from starvation in C++11 的回答说我们应该使用互斥锁。我尝试过这个。它可以持续更长时间,但有时仍会冻结(很少)。我认为互斥锁也不是防止线程饥饿的明确提示。 (也许我做错了?)

现在我同时使用互斥锁和禁用 Windows 优先级提升功能,但我根本不喜欢这个解决方案。谁能提供一个无饥饿的生产者和消费者的例子(在 C++11 中更好)?

制作人:

while(Streamer.IsConnected()) {
    uint8_t *pBits = Streamer.AcquireNext();
    // The buffer is full
    if(pBits && get_counter(&fps_limiter) >= 1000 / args.m_MaxFps && check_target_window(args.m_TargetWindow.c_str(), limit, &rect)) {
        BROFILER_FRAME("MainLoop")
        start_counter(&fps_limiter);
        if(!FAILED(capture_screen(g_DXGIManager, rect, pBits)))
            Streamer.PushNext();
    }
    else {
        this_thread::yield();
        // lower cpu usage
        Sleep(1);
        continue;
    }

    if (get_counter(&bit_rate) >= 1000) {
        uint32_t bps = Streamer.GetBitRate();
        printf("\rBirate: %u bps, %u Bps\t\t\t\t\t", bps, bps/8);
        start_counter(&bit_rate);
    }
}

消费者:

    while(!m_ServerShouldStop) {
        uint8_t *data = AcquireLast();
        if (!data) {
            this_thread::yield();
            Sleep(1);
            continue;
        }
        // encoder callback
        uint8_t *out;
        uint32_t size = m_Encoder(data, &out);

        PopLast();

        // If encoder output something, send it immediately
        if(size>0) {
            // send the size of buffer
            int res1 = ::send_whole_buffer(client_sck, reinterpret_cast<uint8_t *>(&size),
                sizeof(size));
            // then the contents
            int res2 = ::send_whole_buffer(client_sck, out, size);

            bytes += size;

            if (m_EventHandler)
                m_EventHandler->onFrameSent();

            // If any of them fails....
            if(!res1||!res2)
                break;
        }
        if (get_counter(&counter) >= 1000) {
            m_Bps = bytes * 8;
            bytes = 0;
            start_counter(&counter);
        }

    }
...

最初我没有对循环队列做任何保护。我认为没有竞争条件(一个生产者和一个消费者)。然后我尝试添加互斥体,但没有任何改变......

【问题讨论】:

  • 我建议对线程正在执行的操作进行堆栈跟踪。如果一个线程位于像 WaitForSingleObject 这样的函数中,它应该会给你一个很大的线索。确保连续执行几个堆栈跟踪。
  • 我使用了一个名为 brofiler 的分析器(在 github 上可用)。它将挂钩 Windows APII。我没有明确调用 WaitForSingleObject。但似乎线程会进入它。 (但它会返回)
  • 嗯,不,如果它真的是一个饥饿问题,那么删除提升功能就是错误的做法。你隐藏了一个死锁问题,非常糟糕的主意。生产者-消费者锁很容易从 Boost 和 winapi(InitializeSRWLock 等)中获得,不要自己编写。
  • 在 C++11 锁中可用吗?
  • 我提供上面的代码,如果有什么遗漏请告诉我

标签: c++ multithreading c++11 starvation


【解决方案1】:

冻结这个词意味着竞争条件而不是线程饥饿。

线程饥饿是指所有相关线程都在竞争一个互斥锁,并且一个线程(或几个线程)不断地抢夺互斥锁,而其他线程则处于饥饿状态。如果您对单个 Mutex 有如此多的竞争,那么这是一个糟糕的应用程序设计示例。

不过你说的是冻结。因此,冻结意味着您最终处于竞争状态,其中(两个或更多)线程都无法获得互斥锁或代码中的其他约束。

您的问题中没有足够的信息来提供任何有价值的答案。请提供一个代码示例,说明您正在做什么以及正在发生什么。

【讨论】:

  • 我认为不会出现竞争情况。因为只有一个生产者和一个消费者。(但我会再检查一次)我暂停我的程序并查看每个线程运行到哪里。 “冻结”线程在随机行停止。
  • 嗨蒂姆。冻结代码的可能性是巨大的。这可能是您的循环队列(流媒体)的竞争条件。可能是您的屏幕捕获路由的资源问题导致应用程序不稳定。可能是您的捕获缓冲区正在泄漏内存,这意味着您的循环队列可能不是真正的循环。对不起,有太多的可能性。
  • 我知道但很抱歉我不能在这里提供完整的源代码。
【解决方案2】:

我发现我的局部变量被我同事的函数破坏了。使 libx264 无法正常工作。实际上代码可以写成无锁的。不过,添加互斥锁总比忙着等待要好。可以大大降低cpu使用率

【讨论】:

  • 恕我直言,我不认为您是并行编程专家。如果您有多个线程同时访问相同的数据,请帮自己一个忙并使用锁。
  • 谢谢。后来我还是给我的循环队列加了一个锁。
猜你喜欢
  • 2013-04-01
  • 2012-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-05
  • 2016-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多