2016-10-31 3 views
0

Я пишу сервер облачных игр с нулевой задержкой. Это программный конвейер. На первом этапе мы снимаем экран, а на втором этапе мы кодируем его на видео.Как предотвратить потоковое голодание

Однако после некоторого количества времени вторая ступень замерзает. Я пробовал много платформенно-независимый подход, но вены, в конце концов, они заморозятся. В ответе 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); 
     } 

    } 
... 

Изначально я не делал никакой защиты для круговой очереди. Я думаю, что нет условий гонки (один производитель и один потребитель). Затем я пытаюсь добавить мьютексы, но ничего не меняется ....

+1

Я бы предложил взять трассировку стека из того, что делают потоки. Если поток работает в функции типа WaitForSingleObject, он должен дать вам большой ключ. Сделайте несколько следов стека подряд. – Steve

+0

Я использую профилировщик, называемый brofiler (доступен на github). Он подключит Windows APiI. Я явно не называл WaitForSingleObject. но кажется, что поток попадет в него. (Но он вернется назад) –

+3

Хм, нет, если это действительно проблема голодания, то удаление функции повышения - это совсем не то, что нужно сделать. Вы скрываете тупиковую проблему, очень плохую идею. Блокировки производителя и потребителя легко доступны из Boost и winapi (InitializeSRWLock и т. Д.), Не пишите свои собственные. –

ответ

2

Слово «замораживание» подразумевает условие гонки, а не голодание.

Головоломка с резьбой - это то, где все соответствующие темы конкурируют за один мьютекс, и одна нить (или несколько потоков) продолжает захватывать мьютекс, оставляя другие потоки голодающими. Это пример плохого дизайна приложения, если у вас есть такая конкуренция за один Mutex.

Однако вы сказали, что замерзаете. Таким образом, замораживание подразумевает, что вы оказались в состоянии Race, в котором ни один из (двух или более) потоков не может получить мьютекс или какое-либо другое ограничение в вашем коде.

В вашем вопросе недостаточно информации, чтобы дать какой-либо стоящий ответ. Пожалуйста, предоставьте пример кода, который именно вы делаете, и что именно происходит.

+0

ОК, я предоставлю свой код как можно скорее –

+0

Я не думаю, что состояние гонки происходит. потому что есть только один производитель и один потребитель. (но я проверю его еще раз). Я приостанавливаю свою программу и вижу, к чему идет каждый поток. «замороженный» поток останавливается в случайной строке. –

+0

Привет Тим. Количество возможностей для замораживания вашего кода огромно. Это может быть состояние гонки с вашей круговой очередью (стример). Возможно, проблема с ресурсами с вашей маршрутизацией экрана делает приложение неустойчивым. может быть, что ваш буфер захвата является утечкой памяти, возможно, ваша круговая очередь не является круговой. Извините, но есть слишком много возможностей. – tcwicks

0

Я обнаружил, что моя локальная переменная повреждена функцией моего коллеги. Создание libx264 работает некорректно. На самом деле код можно написать без блокировки. stiil, добавление мьютекса лучше, чем ожидание. может снизить частоту использования процессора

+1

При всем моем уважении у меня нет впечатления, что вы специалист по параллельному программированию. Если у вас есть несколько потоков, обращающихся к одним и тем же данным одновременно, сделайте себе одолжение и используйте блокировки. –

+0

Спасибо. Я по-прежнему добавил блокировку в свою круговую очередь позже. –

Смежные вопросы