2016-10-31 7 views
0

J'écris un serveur de jeu de nuage de latence zéro. C'est un pipeline de logiciels. Dans la première étape, nous capturons l'écran et dans la deuxième étape, nous l'encodons à la vidéo.Comment éviter la famine de fil

Cependant, après un certain laps de temps, la deuxième étape gèle. J'ai essayé de nombreuses approches indépendantes de la plate-forme, mais dans la veine, l'une ou l'autre gèlera éventuellement. La réponse de How to prevent threads from starvation in C++11 a déclaré que nous devrions utiliser mutex. Je l'ai essayé. Et ça peut durer plus longtemps mais ça gèle parfois (rarement). Je pense que le mutex n'est pas un indice explicite pour empêcher la famine de fil aussi. (peut-être que je fais mal?)

Maintenant, j'utilise mutex et désactiver la fonctionnalité de priorité de Windows en même temps, mais je n'aime pas du tout cette solution. Quelqu'un pourrait-il fournir un exemple de producteur et consommateur sans faim (mieux en C++ 11)?

producteur:

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); 
    } 
} 

consommateur:

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); 
     } 

    } 
... 

Au départ, je n'ai pas fait la protection de la file d'attente circulaire. Je pense qu'il n'y a pas de condition de concurrence (un producteur et un consommateur). Puis j'essaye d'ajouter le mutex mais rien ne change ...

+1

Je suggère de prendre des traces de pile de ce que font les threads. Si un thread est assis dans une fonction comme WaitForSingleObject, cela devrait vous donner un indice. Faites quelques traces de pile dans une rangée pour être sûr. – Steve

+0

J'utilise un profileur appelé brofiler (disponible sur github). Il va accrocher Windows APiI. Je n'ai pas appelé explicitement WaitForSingleObject. mais il semble que le fil va y entrer. (Mais il reviendra) –

+3

Hmm, non, si * vraiment * était un problème de famine, alors supprimer la fonction boost est exactement la mauvaise chose à faire. Vous cachez un problème de blocage, très mauvaise idée. Les verrous producteur-consommateur sont facilement disponibles chez Boost et le winapi (InitializeSRWLock etc), n'écrivez pas les vôtres. –

Répondre

2

Le mot Gel implique une condition de course plutôt que la famine de fil.

La privation de fil est l'endroit où tous les fils concernés sont en concurrence pour un seul mutex et un seul fil (ou quelques-uns des fils) continuent de saisir le mutex laissant les autres fils affamés. Ceci est un exemple d'une mauvaise conception d'application si vous avez beaucoup de concurrence pour un seul Mutex.

Cependant, vous avez dit Congélation. Donc, la congélation implique que vous avez fini dans une condition de Race où aucun des threads (deux ou plus) ne peut obtenir le mutex ou une autre contrainte dans votre code.

Il n'y a pas assez d'informations dans votre question pour fournir une réponse valable. S'il vous plaît fournir un échantillon de code de exactement ce que vous faites et exactement ce qui se passe.

+0

ok je vais fournir mon code dès que possible –

+0

Je ne pense pas que l'état de la concurrence se produit. car il n'y a qu'un seul producteur et un seul consommateur (mais je vais le vérifier une fois de plus). Je fais une pause dans mon programme et je vois où va chaque fil. le fil "gelé" s'arrête sur une ligne aléatoire. –

+0

Bonjour Tim. Le nombre de possibilités de ce qui gèle votre code est énorme. Ce pourrait être une condition de course avec votre file circulaire (streamer). Il se peut qu'un problème de ressources avec votre routage de capture d'écran rende l'application instable. il se peut que votre mémoire tampon de capture laisse fuir la mémoire, ce qui signifie que votre file circulaire n'est pas vraiment circulaire. Désolé mais il y a beaucoup trop de possibilités. – tcwicks

0

J'ai trouvé que ma variable locale est corrompue par la fonction de mon collègue. Faire fonctionner libx264 de manière incorrecte. En fait, le code peut être écrit sans verrou. stiil, ajouter mutex est mieux que occupé en attente. peut réduire beaucoup l'utilisation du processeur

+1

Avec tout le respect que je vous dois, je n'ai pas l'impression que vous êtes un expert en programmation parallèle. Si vous avez plusieurs threads accédant simultanément aux mêmes données, faites-vous plaisir et utilisez des verrous. –

+0

Merci. J'ai encore ajouté un verrou à ma file d'attente circulaire plus tard. –