我正在编写零延迟云游戏服务器。这是一个软件管道。在第一阶段,我们捕获屏幕,在第二阶段,我们将其编码为视频。
然而,经过一段时间后,第二阶段会冻结。我尝试了许多平台无关的方法,但是他们中的任何一个都会最终冻结。 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);
}
}
...
最初我没有对循环队列做任何保护。我认为没有竞争条件(一个生产者和一个消费者)。然后我尝试添加互斥锁,但没有任何改变......
答案 0 :(得分:2)
冷冻这个词意味着竞争条件而不是线索饥饿。
线程饥饿是所有相关线程都在竞争单个互斥锁的地方,而单个线程(或一些线程)继续抓住互斥锁而其他线程被饿死。如果您对单个Mutex有太多的竞争,那么这就是一个糟糕的应用程序设计的例子。
然而你说冻结。因此,冻结意味着您已经达到了Race条件,其中(两个或更多)线程都不能获取代码中的互斥锁或其他约束。
您的问题中没有足够的信息可以提供任何有价值的答案。请提供一份代码示例,说明您正在做什么以及确切地发生了什么。
答案 1 :(得分:0)
我发现我的同事的功能损坏了我的局部变量。使libx264工作不正常。实际上代码可以无锁写。 stiil,添加互斥量比忙碌等待更好。可以大大降低CPU使用率