проблемы планирования - PullRequest
2 голосов
/ 31 августа 2010

У меня есть два потока в шаблоне производитель-потребитель. Код работает, но тогда поток потребителя будет голодать, а поток производителя - голодать.

При работе программа выводит:

Send Data...semValue = 1
Recv Data...semValue = 0
Send Data...semValue = 1
Recv Data...semValue = 0
Send Data...semValue = 1
Recv Data...semValue = 0

Затем что-то меняется и потоки истощаются, программа выводит:

Send Data...semValue = 1
Send Data...semValue = 2
Send Data...semValue = 3
...
Send Data...semValue = 256
Send Data...semValue = 257
Send Data...semValue = 258
Recv Data...semValue = 257
Recv Data...semValue = 256
Recv Data...semValue = 255
...
Recv Data...semValue = 0
Send Data...semValue = 1
Recv Data...semValue = 0
Send Data...semValue = 1
Recv Data...semValue = 0

Я знаю, что потоки планируются ОС и могут запускаться с разной скоростью и в произвольном порядке. Мой вопрос: когда я делаю YieldThread (звонит pthread_yield ), разве Talker не должен давать Слушателю возможность бегать? Почему я получаю это странное расписание?

Фрагмент кода ниже. Класс потока и класс семафора являются классами абстракций. Я пошел дальше, когда убрал очередь для передачи данных между потоками, чтобы я мог исключить эту переменную.

const int LOOP_FOREVER = 1;

class Listener : public Thread
{
   public:
      Listener(Semaphore* dataReadySemaphorePtr)
         : Thread("Listener"),
           dataReadySemaphorePtr(dataReadySemaphorePtr)
      {
         //Intentionally left blank.
      }

   private:
      void ThreadTask(void)
      {
         while(LOOP_FOREVER)
         {
            this->dataReadySemaphorePtr->Wait();
            printf("Recv Data...");
            YieldThread();
         }
      }

      Semaphore*  dataReadySemaphorePtr;
};


class Talker : public Thread
{
   public:
      Talker(Semaphore* dataReadySemaphorePtr)
         : Thread("Talker"),
           dataReadySemaphorePtr(dataReadySemaphorePtr)
      {
         //Intentionally left blank
      }

   private:
      void ThreadTask(void)
      {
         while(LOOP_FOREVER)
         {
            printf("Send Data...");
            this->dataReadySemaphorePtr->Post();
            YieldThread();
         }
      }

      Semaphore*  dataReadySemaphorePtr;
};


int main()
{
   Semaphore  dataReadySemaphore(0);

   Listener   listener(&dataReadySemaphore);
   Talker     talker(&dataReadySemaphore);

   listener.StartThread();
   talker.StartThread();

   while (LOOP_FOREVER); //Wait here so threads can run
}

Ответы [ 2 ]

2 голосов
/ 31 августа 2010

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

В многопоточной среде вы никогда не можете делать предположения о том, как будет планироваться время процессора; если вам нужно обеспечить правильное поведение, используйте блокировку.

1 голос
/ 31 августа 2010

Верьте или нет, он работает так, потому что он более эффективен.Каждый раз, когда процессор переключается между потоками, он выполняет переключение контекста, которое тратит определенное количество времени.Мой совет - отпустить его, если у вас нет другого требования, такого как максимальная задержка или размер очереди, и в этом случае вам нужен еще один семафор для «готовности к большему количеству данных» в дополнение к вашему «данным, готовым для прослушивания».

...