Я пишу сервер облачных игр с нулевой задержкой. Это программный конвейер. На первом этапе мы снимаем экран, а на втором этапе мы кодируем его на видео.Как предотвратить потоковое голодание
Однако после некоторого количества времени вторая ступень замерзает. Я пробовал много платформенно-независимый подход, но вены, в конце концов, они заморозятся. В ответе 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, он должен дать вам большой ключ. Сделайте несколько следов стека подряд. – Steve
Я использую профилировщик, называемый brofiler (доступен на github). Он подключит Windows APiI. Я явно не называл WaitForSingleObject. но кажется, что поток попадет в него. (Но он вернется назад) –
Хм, нет, если это действительно проблема голодания, то удаление функции повышения - это совсем не то, что нужно сделать. Вы скрываете тупиковую проблему, очень плохую идею. Блокировки производителя и потребителя легко доступны из Boost и winapi (InitializeSRWLock и т. Д.), Не пишите свои собственные. –