QT 中的生产者和消费者信号量

为了高效实现环形缓冲区的生产者和消费者的异步通信操作。我们需要用Qt中的QWaitCondition 或者QSemaphore

本文介绍一下Qt中关于这两个类的例子。(Qt4.7)

QWaitCondition介绍


QWaitCondition Class Reference

The QWaitCondition class provides a condition variable for synchronizing threads.

  1. #include <QWaitCondition>

Note: All functions in this class are thread-safe.

Detailed Description

The QWaitCondition class provides a condition variable for synchronizing threads.

QWaitCondition allows a thread to tell other threads that some sort of condition has been met. One or many threads can block waiting for a QWaitCondition to set a condition with wakeOne() or wakeAll(). Use wakeOne() to wake one randomly selected condition or wakeAll() to wake them all.

For example, let's suppose that we have three tasks that should be performed whenever the user presses a key. Each task could be split into a thread, each of which would have a run() body like this:

  1. forever   {
  2.     mutex. lock ( ) ;
  3.     keyPressed. wait ( &mutex ) ;
  4.     do_something ( ) ;
  5.     mutex. unlock ( ) ;
  6. }

Here, the keyPressed variable is a global variable of type QWaitCondition.

A fourth thread would read key presses and wake the other three threads up every time it receives one, like this:

  1. forever   {
  2.      getchar ( ) ;
  3.     keyPressed. wakeAll ( ) ;
  4. }

The order in which the three threads are woken up is undefined. Also, if some of the threads are still in do_something() when the key is pressed, they won't be woken up (since they're not waiting on the condition variable) and so the task will not be performed for that key press. This issue can be solved using a counter and a QMutex to guard it. For example, here's the new code for the worker threads:

  1. forever   {
  2.     mutex. lock ( ) ;
  3.     keyPressed. wait ( &mutex ) ;
  4.      ++count ;
  5.     mutex. unlock ( ) ;
  6.  
  7.     do_something ( ) ;
  8.  
  9.     mutex. lock ( ) ;
  10.      --count ;
  11.     mutex. unlock ( ) ;
  12. }

Here's the code for the fourth thread:

  1. forever   {
  2.      getchar ( ) ;
  3.  
  4.     mutex. lock ( ) ;
  5.      // Sleep until there are no busy worker threads
  6.      while  (count  >  0 )   {
  7.         mutex. unlock ( ) ;
  8.         sleep ( 1 ) ;
  9.         mutex. lock ( ) ;
  10.      }
  11.     keyPressed. wakeAll ( ) ;
  12.     mutex. unlock ( ) ;
  13. }

The mutex is necessary because the results of two threads attempting to change the value of the same variable simultaneously are unpredictable.

Wait conditions are a powerful thread synchronization primitive. The Wait Conditions example shows how to use QWaitCondition as an alternative to QSemaphore for controlling access to a circular buffer shared by a producer thread and a consumer thread.

See also QMutexQSemaphoreQThread, and Wait Conditions Example.

Public Functions

Toggle details QWaitCondition

QWaitCondition()

Toggle details QWaitCondition

~QWaitCondition()

Toggle details boolQWaitCondition

wait(QMutex *mutex , unsigned longtime=ULONG_MAX...)

Toggle details boolQWaitCondition

wait(QReadWriteLock *readWriteLock , unsigned longtime=ULONG_MAX...)

Toggle details voidQWaitCondition

wakeAll()

Toggle details voidQWaitCondition

wakeOne()

QWaitCondition完整例子:

  1. #include <QtCore>
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5.  
  6. const   int   DataSize   =   100000 ;
  7. const   int   BufferSize   =   8192 ;
  8. char  buffer [ BufferSize ] ;
  9.  
  10. QWaitCondition  bufferNotEmpty ;
  11. QWaitCondition  bufferNotFull ;
  12. QMutex  mutex ;
  13. int  numUsedBytes  =   0 ;
  14.  
  15. class   Producer   :   public   QThread
  16.   {
  17. public :
  18.      void  run ( ) ;
  19. } ;
  20.  
  21. void   Producer :: run ( )
  22.   {
  23.     qsrand ( QTime ( 0 , 0 , 0 ) . secsTo ( QTime :: currentTime ( ) ) ) ;
  24.  
  25.      for   ( int  i  =   0 ;  i  <   DataSize ;   ++ i )    {
  26.         mutex . lock ( ) ;
  27.          if   ( numUsedBytes  ==   BufferSize )
  28.             bufferNotFull . wait ( & mutex ) ;
  29.         mutex . unlock ( ) ;
  30.  
  31.         buffer [ %   BufferSize ]   =   "ACGT" [ ( int ) qrand ( )   %   4 ] ;
  32.  
  33.         mutex . lock ( ) ;
  34.          ++ numUsedBytes ;
  35.         bufferNotEmpty . wakeAll ( ) ;
  36.         mutex . unlock ( ) ;
  37.      }
  38. }
  39.  
  40. class   Consumer   :   public   QThread
  41.   {
  42. public :
  43.      void  run ( ) ;
  44. } ;
  45.  
  46. void   Consumer :: run ( )
  47.   {
  48.      for   ( int  i  =   0 ;  i  <   DataSize ;   ++ i )    {
  49.         mutex . lock ( ) ;
  50.          if   ( numUsedBytes  ==   0 )
  51.             bufferNotEmpty . wait ( & mutex ) ;
  52.         mutex . unlock ( ) ;
  53.  
  54.          fprintf ( stderr ,   "%c" ,  buffer [ %   BufferSize ] ) ;
  55.  
  56.         mutex . lock ( ) ;
  57.          -- numUsedBytes ;
  58.         bufferNotFull . wakeAll ( ) ;
  59.         mutex . unlock ( ) ;
  60.      }
  61.      fprintf ( stderr ,   "\n" ) ;
  62. }
  63.  
  64. int  main ( int  argc ,   char   * argv [ ] )
  65.   {
  66.      QCoreApplication  app ( argc ,  argv ) ;
  67.      Producer  producer ;
  68.      Consumer  consumer ;
  69.     producer . start ( ) ;
  70.     consumer . start ( ) ;
  71.     producer . wait ( ) ;
  72.     consumer . wait ( ) ;
  73.      return   0 ;
  74. }

QSemaphore介绍


QSemaphore Class Reference

The QSemaphore class provides a general counting semaphore.

  1. #include <QSemaphore>

Note: All functions in this class are thread-safe.

Detailed Description

The QSemaphore class provides a general counting semaphore.

A semaphore is a generalization of a mutex. While a mutex can only be locked once, it's possible to acquire a semaphore multiple times. Semaphores are typically used to protect a certain number of identical resources.

Semaphores support two fundamental operations, acquire() and release():

  • acquire(n) tries to acquire n resources. If there aren't that many resources available, the call will block until this is the case.
  • release(n) releases n resources.

There's also a tryAcquire() function that returns immediately if it cannot acquire the resources, and an available() function that returns the number of available resources at any time.

Example:

  1. QSemaphore sem ( 5 ) ;       // sem.available() == 5
  2.  
  3. sem. acquire ( 3 ) ;          // sem.available() == 2
  4. sem. acquire ( 2 ) ;          // sem.available() == 0
  5. sem. release ( 5 ) ;          // sem.available() == 5
  6. sem. release ( 5 ) ;          // sem.available() == 10
  7.  
  8. sem. tryAcquire ( 1 ) ;       // sem.available() == 9, returns true
  9. sem. tryAcquire ( 250 ) ;     // sem.available() == 9, returns false

A typical application of semaphores is for controlling access to a circular buffer shared by a producer thread and a consumer thread. TheSemaphores example shows how to use QSemaphore to solve that problem.

A non-computing example of a semaphore would be dining at a restaurant. A semaphore is initialized with the number of chairs in the restaurant. As people arrive, they want a seat. As seats are filled, available() is decremented. As people leave, the available() is incremented, allowing more people to enter. If a party of 10 people want to be seated, but there are only 9 seats, those 10 people will wait, but a party of 4 people would be seated (taking the available seats to 5, making the party of 10 people wait longer).

See also QMutexQWaitConditionQThread, and Semaphores Example.

Public Functions

Toggle details QSemaphore

QSemaphore(intn=0)

Toggle details QSemaphore

~QSemaphore()

Toggle details voidQSemaphore

acquire(intn=1)

Toggle details intQSemaphore

available()const

Toggle details voidQSemaphore

release(intn=1)

Toggle details boolQSemaphore

tryAcquire(intn=1)

Toggle details boolQSemaphore

tryAcquire(intn , inttimeout)

    You may use the documentation under the terms of the  GNU Free Documentation License version 1.3, as published by the Free Software Foundation. Alternatively, you may use the documentation in accordance with the terms contained in a written agreement between you and Digia.
    QSemaphore完整例子:

    1. #include <QtCore>
    2.  
    3. #include <stdio.h>
    4. #include <stdlib.h>
    5.  
    6. const   int   DataSize   =   100000 ;
    7. const   int   BufferSize   =   8192 ;
    8. char  buffer [ BufferSize ] ;
    9.  
    10. QSemaphore  freeBytes ( BufferSize ) ;
    11. QSemaphore  usedBytes ;
    12.  
    13. class   Producer   :   public   QThread
    14.   {
    15. public :
    16.      void  run ( ) ;
    17. } ;
    18.  
    19. void   Producer :: run ( )
    20.   {
    21.     qsrand ( QTime ( 0 , 0 , 0 ) . secsTo ( QTime :: currentTime ( ) ) ) ;
    22.      for   ( int  i  =   0 ;  i  <   DataSize ;   ++ i )    {
    23.         freeBytes . acquire ( ) ;
    24.         buffer [ %   BufferSize ]   =   "ACGT" [ ( int ) qrand ( )   %   4 ] ;
    25.         usedBytes . release ( ) ;
    26.      }
    27. }
    28.  
    29. class   Consumer   :   public   QThread
    30.   {
    31. public :
    32.      void  run ( ) ;
    33. } ;
    34.  
    35. void   Consumer :: run ( )
    36.   {
    37.      for   ( int  i  =   0 ;  i  <   DataSize ;   ++ i )    {
    38.         usedBytes . acquire ( ) ;
    39.          fprintf ( stderr ,   "%c" ,  buffer [ %   BufferSize ] ) ;
    40.         freeBytes . release ( ) ;
    41.      }
    42.      fprintf ( stderr ,   "\n" ) ;
    43. }
    44.  
    45. int  main ( int  argc ,   char   * argv [ ] )
    46.   {
    47.      QCoreApplication  app ( argc ,  argv ) ;
    48.      Producer  producer ;
    49.      Consumer  consumer ;
    50.     producer . start ( ) ;
    51.     consumer . start ( ) ;
    52.     producer . wait ( ) ;
    53.     consumer . wait ( ) ;
    54.      return   0 ;
    55. }


    FROM: http://blog.163.com/qimo601@126/blog/static/1582209320121118112828662/


    Qt提供了QSemaphore类来实现信号量功能,可以用于实现一个生产者多个消费者的场景。 首先,我们需要创建一个QSemaphore对象,作为生产者消费者之间共享的信号量。在这个例子,我们假设生产者线程负责生产产品,消费者线程负责消费产品。 生产者的逻辑如下: 1. 首先,获取信号量的锁定,如果信号量的计数器为0,则阻塞等待信号量的释放; 2. 当信号量的计数器不为0时,生产者线程开始生产产品,并进行相应的操作; 3. 完成产品的生产后,释放信号量的锁定,并增加信号量的计数器。 消费者的逻辑如下: 1. 首先,获取信号量的锁定,如果信号量的计数器为0,则阻塞等待信号量的释放; 2. 当信号量的计数器不为0时,消费者线程开始消费产品,并进行相应的操作; 3. 完成产品的消费后,释放信号量的锁定,并增加信号量的计数器。 下面是一个简化的示例代码: ```cpp QSemaphore semaphore; // 创建信号量对象 QVector<QString> products; // 存放产品的容器 // 生产者线程函数 void producer() { while (true) { semaphore.acquire(); // 获取信号量的锁定 // 生产产品的操作 QString product = generateProduct(); products.append(product); semaphore.release(); // 释放信号量的锁定 QThread::sleep(1); // 等待一段时间 } } // 消费者线程函数 void consumer() { while (true) { semaphore.acquire(); // 获取信号量的锁定 // 消费产品的操作 if (!products.isEmpty()) { QString product = products.takeFirst(); consumeProduct(product); } semaphore.release(); // 释放信号量的锁定 QThread::sleep(1); // 等待一段时间 } } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建生产者消费者线程 QThread producerThread; QThread consumerThread1; QThread consumerThread2; QThread consumerThread3; // 信号量关联线程 semaphore.setInitialValue(1); semaphore.moveToThread(&producerThread); semaphore.moveToThread(&consumerThread1); semaphore.moveToThread(&consumerThread2); semaphore.moveToThread(&consumerThread3); // 启动线程 producerThread.start(); consumerThread1.start(); consumerThread2.start(); consumerThread3.start(); return a.exec(); } ``` 在上面的示例代码,我们创建了一个QSemaphore对象作为信号量,并使用`acquire()`和`release()`函数来获取和释放信号量的锁定。生产者线程通过`acquire()`函数获取信号量的锁定,如果信号量的计数器为0,则线程会阻塞等待信号量的释放。消费者线程也使用相同的方法来获取和释放信号量的锁定。 这种方式实现了一个生产者多个消费者的场景,多个消费者线程可以在信号量的控制下轮流进行产品的消费。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值