Qt数据同步与控制

1. qt多线程中的数据同步与互斥

qt实现互斥与同步的常用类有QMutex、QMutexLocker、QReadWriteLock、QReadLocker、QWriteLocker、QSemaphore、QWaitCondition。

2. 使用

  • QMutex,对同一代码块进行操作时用,但是会产生死锁

      lock(),加锁,阻塞当前线程
      unlock(),解锁
      trylock(),如果已加锁,则立即返回,非阻塞
    
  • QMutexLocker,对死锁的优化,只用写一句话,在析构中自动解锁

      用一个QMutex作为构造参数
      QMutex  _mutex; 		
      void Key::run()
      {
          QMutexLocker lock(&_mutex);
          key++;
          ...
      }
    
  • QReadWriteLock,对读写操作进行加锁

      QReadWriteLock lock;		
      void ReaderThread::run()
      {
          ...
          lock.lockForRead();
          read_file();
          lock.unlock();
          ...
      }
    	
      void WriterThread::run()
      {
          ...
          lock.lockForWrite();
          write_file();
          lock.unlock();
          ...
      }
    
  • QSemaphore,信号量,可以获取多次,用来保护一定数量的同种资源。不能获取时,则阻塞线程直到满足条件。用完可以释放。解决生产-消费问题中二者同时分别操作缓冲区的不同部分,效率很高。初始化信号量为0,大于0则可用,否则不可用

      const int DataSize = 1000;
      const int BufferSize = 100;
      extern char buffer[BufferSize];
    	
      extern QSemaphore freeBytes;//已消费,待生产
      extern QSemaphore usedBytes; //已生产,待消费
    	
      class Producer : public QThread
      {
          Q_OBJECT
    	
      public:
          Producer(QObject *parent=0);
          ~Producer();
    	
      void run()
      {
          qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
          for (int i = 0; i < DataSize; ++i) {
              freeBytes.acquire();
              buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
              usedBytes.release();
          }
      }
    		
      };
    	
      class Consumer : public QThread
      {
          Q_OBJECT
    	
      public:
          Consumer(QObject *parent=0);
          ~Consumer();
    	
          void run()
          {
              QByteArray str;
              for (int i = 0; i < DataSize; ++i) {
                  usedBytes.acquire();
                  str.append(buffer[i % BufferSize]);
                  str.append("\n");
                  freeBytes.release();
              }
              qDebug() <<str.toStdString().c_str();
          }
    	
      };
    

理解:生产者与消费者依次读写数据,生产者优先操作,每次生产一个,消费者也是每次消费一个。读写区域由作者自己控制,最好是环形缓冲区。

  • QWaitCondition

需要与互斥锁配合使用,和信号量一样,可以用于环形缓冲区的生产与消费。

	const int DataSize = 1000;
	
	const int BufferSize = 100;
	char buffer[BufferSize];
	
	QWaitCondition bufferNotEmpty;
	QWaitCondition bufferNotFull;
	QMutex mutex;
	extern int numUsedBytes;// = 0;
	
	class Producer : public QThread
	{
	public:
	    Producer(QObject *parent = NULL) : QThread(parent)
	    {
	    }
	
	    void run() Q_DECL_OVERRIDE
	    {
	        qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
	
	        for (int i = 0; i < DataSize; ++i) {
	            mutex.lock();
	            if (numUsedBytes == BufferSize)
	                bufferNotFull.wait(&mutex);
	            mutex.unlock();
	
	            buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
	
	            mutex.lock();
	            ++numUsedBytes;
	            bufferNotEmpty.wakeAll();
	            mutex.unlock();
	        }
	    }
	};
	
	
	class Consumer : public QThread
	{
	    Q_OBJECT
	public:
	    Consumer(QObject *parent = NULL) : QThread(parent)
	    {
	    }
	
	    void run() Q_DECL_OVERRIDE
	    {
	        for (int i = 0; i < DataSize; ++i) {
	            mutex.lock();
	            if (numUsedBytes == 0)
	                bufferNotEmpty.wait(&mutex);
	            mutex.unlock();
	
	            fprintf(stderr, "%c", buffer[i % BufferSize]);
	
	            mutex.lock();
	            --numUsedBytes;
	            bufferNotFull.wakeAll();
	            mutex.unlock();
	        }
	        fprintf(stderr, "\n");
	    }
	};

参考:

  1. my coding.net
  2. 定义
  3. QT5开发与实例
  4. 信号量与PV操作