pthread调度问题

发布于 2024-09-16 18:16:20 字数 2309 浏览 10 评论 0原文

我有两个采用生产者-消费者模式的线程。代码可以工作,但是消费者线程将陷入饥饿,然后生产者线程将陷入饥饿。

工作时,程序输出:

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不应该给Listener一个运行的机会吗?为什么我会得到这种奇怪的安排?

下面的代码片段。 Thread类和Semaphore类是抽象类。我继续剥离了线程之间数据传递的队列,这样我就可以消除该变量。

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
}

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜心小果奶 2024-09-23 18:16:21

不会。除非您使用锁来阻止它,否则即使一个线程产生它的量程,也不需要另一个线程接收下一个量程。

在多线程环境中,您永远无法对如何安排处理器时间做出假设;如果您需要强制执行正确的行为,请使用锁。

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.

玻璃人 2024-09-23 18:16:21

不管你相信与否,它以这种方式运行是因为这样效率更高。处理器每次在线程之间切换时,都会执行一次上下文切换,这会浪费一定的时间。我的建议是放弃它,除非您有另一个要求,例如最大延迟或队列大小,在这种情况下,除了“准备好侦听的数据”之外,您还需要另一个信号量来“准备好更多数据”。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文