开发者

pthread scheduling problems

开发者 https://www.devze.com 2023-01-14 08:20 出处:网络
I have two threads in a producer-consumer pattern. Code works, but then the consumer thread will get starved, and then the producer thread will get starved.

I have two threads in a producer-consumer pattern. Code works, but then the consumer thread will get starved, and then the producer thread will get starved.

When working, program outputs:

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

Then something changes and threads get starved, program outputs:开发者_开发问答

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

I know threads are scheduled by the OS, and can run at different rates and in random order. My question: When I do a YieldThread(calls pthread_yield), shouldn’t the Talker give Listener a chance to run? Why am I getting this bizarre scheduling?

Snippet of Code below. Thread class and Semaphore class are abstractions classes. I went ahead as stripped out the queue for data passing between the threads so I could eliminate that variable.

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
}


No. Unless you are using a lock to prevent it, even if one thread yields it's quantum, there's no requirement that the other thread receives the next quantum.

In a multithreaded environment, you can never ever ever make assumptions about how processor time is going to be scheduled; if you need to enforce correct behavior, use a lock.


Believe it or not, it runs that way because it's more efficient. Every time the processor switches between threads, it performs a context switch that wastes a certain amount of time. My advice is to let it go unless you have another requirement like a maximum latency or queue size, in which case you need another semaphore for "ready for more data" in addition to your "data ready for listening" one.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号