Qt同步线程

我们知道,多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确。Qt提供了一些类来实现线程的同步,如QMutexQMutexLockerQReadWriteLockQReadLockerQWriteLockerQSemaphoreQWaitCondition。下面我们分别来看它们的用法:

QMutex

首先,简单的了解一下QMutex提供的函数。

构造函数:QMutex ( RecursionMode mode = NonRecursive )。

需要注意的是构造函数的参数,RecursionMode 递归模式。枚举类型RecursionMode 有两个值:

QMutex::Recursive,在这个模式下,一个线程可以多次锁同一个互斥量。需要注意的是,调用lock()多少次锁,就必须相应的调用unlock()一样次数解锁。

QMutex::NonRecursive(默认),在这个模式下,一个线程只能锁互斥量一次

void QMutex::lock ()

该函数用来锁住一个互斥量。如果另外的线程已经锁住了互斥量,函数将被阻塞等待另外的线程解锁互斥量。

如果是一个可递归的互斥量,则可以从同一个线程多次调用这个函数,如果是非递归的互斥量,多次调用这个函数将会引发死锁。我们来看看源码是怎么实现的。

  1. void QMutex::lock()
  2.  
  3. {
  4.  
  5.   QMutexPrivate *d = static_cast<QMutexPrivate*>(this->d);
  6.  
  7.   Qt::HANDLE self;
  8.  
  9.   if(d->recursive) {
  10.  
  11.     self = QThread::currentThreadId();
  12.  
  13.     if(d->owner == self) {
  14.  
  15.     ++d->count; //同一个线程多次lock时,仅仅自增count
  16.  
  17.     //当然递归次数太多也会导致栈溢出
  18.  
  19.     Q_ASSERT_X(d->count != , "QMutex::lock", "Overflowin recursion counter");
  20.  
  21.     return;
  22.  
  23.   }
  24.  
  25.   bool isLocked = d->contenders.testAndSetAcquire(, );
  26.  
  27.   if(!isLocked) {
  28.  
  29.     // didn'tget the lock, wait for it
  30.  
  31.     isLocked = d->wait();
  32.  
  33.     Q_ASSERT_X(isLocked, "QMutex::lock",
  34.  
  35.     "Internalerror, infinite wait has timed out.");
  36.  
  37.   }
  38.  
  39.   d->owner = self; //递归模式时,owner记录拥有互斥量的线程
  40.  
  41.   ++d->count; //记录lock的次数
  42.  
  43.   Q_ASSERT_X(d->count != , "QMutex::lock", "Overflowin recursion counter");
  44.  
  45.   return;
  46.  
  47. }
  48.  
  49. //非递归模式时,
  50.  
  51.   bool isLocked = d->contenders.testAndSetAcquire(, ); //尝试加锁
  52.  
  53.   if(!isLocked) {
  54.  
  55.     lockInternal(); //加锁失败则在lockInternal()中一直等到别的线程解锁。
  56.  
  57.   }
  58.  
  59. }
  60.  
  61. 看看lockInternal的实现
  62.  
  63. void QMutex::lockInternal()
  64.  
  65. {
  66.  
  67. 。。。
  68.  
  69. do {
  70.  
  71. 。。。。//其他代码太复杂,感觉最重要的就是这个while循环了,
  72.  
  73. //一直循环检测,试图加锁。这我们就好理解,非递归模式的//互斥量,不要在同一个线程里,多次调用lock了。因为第二次调用的时候会在
  74.  
  75. //这里死循环了
  76.  
  77. } while(d->contenders != || !d->contenders.testAndSetAcquire(, ));
  78.  
  79. 。。。。。。。
  80.  
  81. }

bool QMutex::tryLock ()

该函数试图锁一个互斥量,如果成功则返回true。如果另外的线程已经锁住了互斥量,函数直接返回false。

bool QMutex::tryLock ( int timeout )

该函数跟上面的trylock()相似。不同的是,如果互斥量在别的线程锁住的情况下,函数会等待timeout 毫秒。需要注意的是,如果传入的timeout 为负数,函数将无限期等待,跟调用lock()一样的效果。这个函数跟上面的差不多,所以只看该函数的源码实现就好了。

  1. bool QMutex::tryLock(inttimeout)
  2.  
  3. {
  4.  
  5. QMutexPrivate *d = static_cast<QMutexPrivate*>(this->d);
  6.  
  7. Qt::HANDLE self;
  8.  
  9. if(d->recursive) {
  10.  
  11. self = QThread::currentThreadId();
  12.  
  13. if(d->owner == self) {
  14.  
  15.   ++d->count;
  16.  
  17.    Q_ASSERT_X(d->count != , "QMutex::tryLock", "Overflow in recursion counter");
  18.  
  19.     return true;
  20.  
  21.     }
  22.  
  23. boolisLocked = d->contenders.testAndSetAcquire(, );
  24.  
  25. if(!isLocked) {
  26.  
  27. // didn'tget the lock, wait for it
  28.  
  29. isLocked = d->wait(timeout); //尝试加锁失败则等待
  30.  
  31. if(!isLocked)
  32.  
  33. return false;
  34.  
  35. }
  36.  
  37. d->owner = self;
  38.  
  39. ++d->count;
  40.  
  41. Q_ASSERT_X(d->count != , "QMutex::tryLock", "Overflow in recursion counter");
  42.  
  43. return true;
  44.  
  45. }
  46.  
  47. //尝试加锁失败,(d->contenders.testAndSetAcquire(0,1)返回false,所以继续执行d->wait(timeout);
  48.  
  49. return (d->contenders.testAndSetAcquire(, ) ||d->wait(timeout));
  50.  
  51. }
  52.  
  53. //在win下,wait函数实际上是用事件对象实现的
  54.  
  55. bool QMutexPrivate::wait(inttimeout)
  56.  
  57. {
  58.  
  59. if(contenders.fetchAndAddAcquire() == ) {
  60.  
  61. // lockacquired without waiting
  62.  
  63. return true;
  64.  
  65. }
  66.  
  67. // 当timeout 小于0,则等待时间为INFINITE,这也就是为什么传负数参数时跟lock一样会无限期等待了
  68.  
  69. boolreturnValue = (WaitForSingleObject(event,timeout < ? INFINITE : timeout) == WAIT_OBJECT_0);
  70.  
  71. contenders.deref();
  72.  
  73. returnreturnValue;
  74.  
  75. }

void QMutex::unlock ()

该函数对互斥量进行解锁。如果在另外的线程加锁,尝试在别的线程进行解锁则会引发错误。试图对没有加锁的互斥量解锁结果是未定义的。

QMutexLocker

QmutexLocker只是为了简化我们对互斥量的加锁和解锁操作。就像智能指针方便我们使用普通指针一样。

QMutexLocker (QMutex * mutex )。

构造函数必须传入一个互斥量指针,然后在构造函数里mutex直接调用lock()。

  1. inline explicitQMutexLocker(QMutex *m)
  2.  
  3. {
  4.  
  5. Q_ASSERT_X((reinterpret_cast<quintptr>(m)& quintptr(1u)) == quintptr(),
  6.  
  7. "QMutexLocker","QMutex pointer is misaligned");
  8.  
  9. if (m){
  10.  
  11. m->lockInline(); // mutex调用lock()加锁
  12.  
  13. val = reinterpret_cast<quintptr>(m)| quintptr(1u);
  14.  
  15. } else{
  16.  
  17. val = ;
  18.  
  19. }
  20.  
  21. }
  22.  
  23. inline ~QMutexLocker() { unlock(); }
  24.  
  25. inline void unlock()
  26.  
  27. {
  28.  
  29. if((val & quintptr(1u)) == quintptr(1u)) {
  30.  
  31. val &= ~quintptr(1u);
  32.  
  33. mutex()->unlockInline(); //析构时调用unlock,确保mutex在离开调用线程时被解锁。
  34.  
  35. }
  36.  
  37. }

下面来看看具体的用法:

假设有个函数有很多return 语句,那么我们就必须记得在每个语句前unlock互斥量,否则互斥量将无法得到解锁,导致其他等待的线程无法继续执行。

  1. int complexFunction(intflag)
  2.  
  3. {
  4.  
  5.   mutex.lock();
  6.  
  7.   int retVal = ;
  8.  
  9.   switch (flag) {
  10.  
  11.     case :
  12.  
  13.     case1:
  14.  
  15.     retVal = moreComplexFunction(flag);
  16.  
  17.     break;
  18.  
  19.   case :
  20.  
  21.   {
  22.  
  23.     int status = anotherFunction();
  24.  
  25.     if (status < ) {
  26.  
  27.       mutex.unlock();
  28.  
  29.       return -;
  30.  
  31.     }
  32.  
  33.     retVal = status + flag;
  34.  
  35.   }
  36.  
  37.   break;
  38.  
  39.   default:
  40.  
  41.   if (flag > ) {
  42.  
  43.     mutex.unlock();
  44.  
  45.     return -;
  46.  
  47.   }
  48.  
  49.   break;
  50.  
  51. }
  52.  
  53. mutex.unlock();
  54.  
  55. return retVal;
  56.  
  57. }

这样的代码显得很冗余又容易出错。如果我们用QMutexLocker

  1. intcomplexFunction(int flag)
  2.  
  3. {
  4.  
  5. QMutexLocker locker(&mutex);
  6.  
  7. int retVal = ;
  8.  
  9. switch (flag) {
  10.  
  11. case :
  12.  
  13. case :
  14.  
  15. return moreComplexFunction(flag);
  16.  
  17. case :
  18.  
  19. {
  20.  
  21. int status = anotherFunction();
  22.  
  23. if (status < )
  24.  
  25. return -;
  26.  
  27. retVal = status + flag;
  28.  
  29. }
  30.  
  31. break;
  32.  
  33. default:
  34.  
  35. if (flag > )
  36.  
  37. return -;
  38.  
  39. break;
  40.  
  41. }
  42.  
  43. return retVal;
  44.  
  45. }

由于locker 是局部变量,在离开函数作用域时,mutex肯定会被解锁。

QreadWriteLock

QreadWriteLock是一个读写锁,主要用来同步保护需要读写的资源。当你想多个读线程可以同时读取资源,但是只能有一个写线程操作资源,而其他线程必须等待写线程完成时,这时候用这个读写锁就很有用了。QreadWriteLock也有递归和非递归模式之分。

我们主要来看看最重要的两个函数是如何实现读写操作的同步的。

void QReadWriteLock::lockForRead ()

该函数lock接了读操作的锁。如果有别的线程已经对lock接了写操作的锁,则函数会阻塞等待。

  1. void QReadWriteLock::lockForRead()
  2.  
  3. {
  4.  
  5.   QMutexLocker lock(&d->mutex);
  6.  
  7.   Qt::HANDLE self = ;
  8.  
  9.   if(d->recursive) {
  10.  
  11.     self = QThread::currentThreadId();
  12.  
  13.     QHash<Qt::HANDLE, int>::iterator it = d->currentReaders.find(self);
  14.  
  15.     if (it!= d->currentReaders.end()) {
  16.  
  17.       ++it.value();
  18.  
  19.       ++d->accessCount;
  20.  
  21.       Q_ASSERT_X(d->accessCount >, "QReadWriteLock::lockForRead()",
  22.  
  23.       "Overflowin lock counter");
  24.  
  25.   return;
  26.  
  27.   }
  28.  
  29. }
  30.  
  31. // accessCount 小于0说明有写线程在操作资源,则阻塞
  32.  
  33. while(d->accessCount < || d->waitingWriters) {
  34.  
  35. ++d->waitingReaders; //自增等待的读线程数
  36.  
  37. d->readerWait.wait(&d->mutex);
  38.  
  39. --d->waitingReaders;
  40.  
  41. }
  42.  
  43. if(d->recursive)
  44.  
  45. d->currentReaders.insert(self, );
  46.  
  47. ++d->accessCount; //自增,记录有多少个线程访问了资源
  48.  
  49. Q_ASSERT_X(d->accessCount > , "QReadWriteLock::lockForRead()", "Overflow in lock counter");
  50.  
  51. }
  52.  
  53. void QReadWriteLock::lockForWrite ()
  54.  
  55. 该函数给lock加了写操作的锁,如果别的线程已经加了读或者写的锁,则函数会被阻塞。
  56.  
  57. void QReadWriteLock::lockForWrite()
  58.  
  59. {
  60.  
  61. QMutexLocker lock(&d->mutex);
  62.  
  63. Qt::HANDLE self = ;
  64.  
  65. if(d->recursive) {
  66.  
  67. self = QThread::currentThreadId();
  68.  
  69. if(d->currentWriter == self) {
  70.  
  71. --d->accessCount;
  72.  
  73. Q_ASSERT_X(d->accessCount <, "QReadWriteLock::lockForWrite()",
  74.  
  75. "Overflowin lock counter");
  76.  
  77. return;
  78.  
  79. }
  80.  
  81. }
  82.  
  83. // accessCount不等于0,说明有线程在操作资源,则函数阻塞等待。
  84.  
  85. // accessCount大于0说明有读线程在读取资源,
  86.  
  87. // accessCount小于0说明有写线程在写数据
  88.  
  89. while(d->accessCount != ) {
  90.  
  91. ++d->waitingWriters; //自增等待的写线程数
  92.  
  93. d->writerWait.wait(&d->mutex);
  94.  
  95. --d->waitingWriters;
  96.  
  97. }
  98.  
  99. if(d->recursive)
  100.  
  101. d->currentWriter = self;
  102.  
  103. --d->accessCount;
  104.  
  105. Q_ASSERT_X(d->accessCount < , "QReadWriteLock::lockForWrite()", "Overflow in lock counter");
  106.  
  107. }

void QReadWriteLock::unlock ()

解锁函数,下面我们看看源码是如何实现,让等待的写线程优先于读线程获得互斥量的锁的。

  1. void QReadWriteLock::unlock()
  2.  
  3. {
  4.  
  5. QMutexLocker lock(&d->mutex);
  6.  
  7. Q_ASSERT_X(d->accessCount != , "QReadWriteLock::unlock()", "Cannot unlock an unlocked lock");
  8.  
  9. boolunlocked = false;
  10.  
  11. if(d->accessCount > ) {
  12.  
  13. // releasinga read lock
  14.  
  15. if(d->recursive) {
  16.  
  17. Qt::HANDLE self =QThread::currentThreadId();
  18.  
  19. QHash<Qt::HANDLE, int>::iterator it =d->currentReaders.find(self);
  20.  
  21. if(it != d->currentReaders.end()) {
  22.  
  23. if(--it.value() <= )
  24.  
  25. d->currentReaders.erase(it);
  26.  
  27. }
  28.  
  29. }
  30.  
  31. // d->accessCount 说明没有线程在操作资源了unlocked为true
  32.  
  33. unlocked = --d->accessCount == ;
  34.  
  35. } else if (d->accessCount < &&++d->accessCount == )
  36.  
  37. {
  38.  
  39. // d->accessCount <0 说明有写线程在操作。则解锁unlocked = true;
  40.  
  41. // released awrite lock
  42.  
  43. unlocked = true;
  44.  
  45. d->currentWriter = ;
  46.  
  47. }
  48.  
  49. //最重要的就是这里
  50.  
  51. if(unlocked) {
  52.  
  53. if(d->waitingWriters) {
  54.  
  55. //如果有写线程在等待,则wake一个写线程。前面我们已经知道,写线程是只
  56.  
  57. //能有一个对资源进行操作的,所以就wakeone了。
  58.  
  59. d->writerWait.wakeOne();
  60.  
  61. } else if (d->waitingReaders) {
  62.  
  63. //如果没有等待的写线程,则wake全部的读线程。因为读线程是可以多个对资源进行操作的。
  64.  
  65. d->readerWait.wakeAll();
  66.  
  67. }
  68.  
  69. }
  70.  
  71. }

下面是我自己简单的实现用例:

  1. class Lock:publicQObject
  2.  
  3. {
  4.  
  5. Q_OBJECT
  6.  
  7. public:
  8.  
  9. Lock();
  10.  
  11. ~Lock();
  12.  
  13. void Start();
  14.  
  15. void Read();
  16.  
  17. void Write();
  18.  
  19. void ReadThread1();
  20.  
  21. void ReadThread2();
  22.  
  23. void WriteThread1();
  24.  
  25. void WriteThread2();
  26.  
  27. protected:
  28.  
  29. private:
  30.  
  31. string strResource;
  32.  
  33. QReadWriteLock lock;
  34.  
  35. };
  36.  
  37. Lock::Lock()
  38.  
  39. {
  40.  
  41. strResource = "Hellworld ......";
  42.  
  43. }
  44.  
  45. Lock::~Lock()
  46.  
  47. {
  48.  
  49. }
  50.  
  51. void Lock::Read()
  52.  
  53. {
  54.  
  55. cout<<"Readdata :"<<strResource<<endl;
  56.  
  57. QEventLoop loop;
  58.  
  59. QTimer::singleShot(,&loop,SLOT(quit()));
  60.  
  61. loop.exec();
  62.  
  63. }
  64.  
  65. void Lock::Write()
  66.  
  67. {
  68.  
  69. strResource = "writelock ";
  70.  
  71. cout<<"Writedata :"<<strResource<<endl;
  72.  
  73. QEventLoop loop;
  74.  
  75. QTimer::singleShot(,&loop,SLOT(quit()));
  76.  
  77. loop.exec();
  78.  
  79. }
  80.  
  81. void Lock::ReadThread1()
  82.  
  83. {
  84.  
  85. lock.lockForRead();
  86.  
  87. cout<<"ReadThread1 lockForRead"<<endl;
  88.  
  89. Read();
  90.  
  91. cout<<"ReadThread1 unlock"<<endl;
  92.  
  93. lock.unlock();
  94.  
  95. }
  96.  
  97. void Lock::ReadThread2()
  98.  
  99. {
  100.  
  101. lock.lockForRead();
  102.  
  103. cout<<"ReadThread2 lockForRead"<<endl;
  104.  
  105. Read();
  106.  
  107. cout<<"ReadThread2 unlock"<<endl;
  108.  
  109. lock.unlock();
  110.  
  111. }
  112.  
  113. void Lock::WriteThread1()
  114.  
  115. {
  116.  
  117. lock.lockForWrite();
  118.  
  119. cout<<"WriteThread1 lockForWrite"<<endl;
  120.  
  121. Write();
  122.  
  123. cout<<"WriteThread1 unlock"<<endl;
  124.  
  125. lock.unlock();
  126.  
  127. }
  128.  
  129. void Lock::WriteThread2()
  130.  
  131. {
  132.  
  133. lock.lockForWrite();
  134.  
  135. cout<<"WriteThread2 lockForWrite"<<endl;
  136.  
  137. Write();
  138.  
  139. cout<<"WriteThread2 unlock"<<endl;
  140.  
  141. lock.unlock();
  142.  
  143. }
  144.  
  145. void Lock::Start()
  146.  
  147. {
  148.  
  149. QtConcurrent::run(this,&Lock::ReadThread1);
  150.  
  151. QtConcurrent::run(this,&Lock::ReadThread2);
  152.  
  153. QtConcurrent::run(this,&Lock::WriteThread1);
  154.  
  155. QtConcurrent::run(this,&Lock::WriteThread2);
  156.  
  157. }

这里我先启动两个读线程,再启动写线程,运行结果如下。我们发现先读线程1先加了锁,读线程1还没解锁的时候,读线程2已经加了锁,验证了读线程是可以同时进入的。

如果我改一下代码:

void Lock::Start()

{

QtConcurrent::run(this,&Lock::WriteThread1);

QtConcurrent::run(this,&Lock::ReadThread1);

QtConcurrent::run(this,&Lock::ReadThread2);

QtConcurrent::run(this,&Lock::WriteThread2);

}

我先启动WriteThread1,然后启动两个读线程,最后启动WriteThread2。运行结果如下,我们发现,WriteThread1运行完之后,先运行WriteThread2,最后才是两个读线程。验证了写线程比读线程先获得锁。

QSemaphore

QSemaphore是提供一个计数的信号量。信号量是泛化的互斥量。一个信号量只能锁一次,但是我们可以多次获得信号量。信号量可以用来同步保护一定数量的资源。

信号量支持两个基本是函数, acquire()和 release():

acquire(n) :尝试获取n个资源。如果没有足够的可用资源,该函数调用会被则是。

release(n) :释放n个资源。

它们的源码实现也很简单:

  1. void QSemaphore::acquire(intn)
  2.  
  3. {
  4.  
  5. Q_ASSERT_X(n >= , "QSemaphore::acquire", "parameter 'n' must be non-negative");
  6.  
  7. QMutexLocker locker(&d->mutex);
  8.  
  9. while (n> d->avail) //申请的资源n 大于可用资源avail则进入等待。
  10.  
  11. d->cond.wait(locker.mutex());
  12.  
  13. d->avail -= n;
  14.  
  15. }
  16.  
  17. void QSemaphore::release(intn)
  18.  
  19. {
  20.  
  21. Q_ASSERT_X(n >= , "QSemaphore::release", "parameter 'n' must be non-negative");
  22.  
  23. QMutexLocker locker(&d->mutex);
  24.  
  25. d->avail += n;
  26.  
  27. d->cond.wakeAll();
  28.  
  29. }

由于avail变量,实际就是一个int的计数变量 。所以我们在调用release()传入的参数n大于信号量初始值也没关系,只是说明可用资源增加了。

例如以下代码:

  1. int main(int argc, char *argv[])
  2.  
  3. {
  4.  
  5. QCoreApplication a(argc, argv);
  6.  
  7. QSemaphore sem();
  8.  
  9. sem.acquire();
  10.  
  11. cout<<"acquire(5); "<<"remaindresource :"<<sem.available()<<endl;
  12.  
  13. sem.release();
  14.  
  15. cout<<"release(5) "<<"remaindresource :"<<sem.available()<<endl;
  16.  
  17. sem.release();
  18.  
  19. cout<<"release(10) "<<"remaindresource :"<<sem.available()<<endl;
  20.  
  21. sem.acquire();
  22.  
  23. cout<<"acquire(15); "<<"remaindresource :"<<sem.available()<<endl;
  24.  
  25. returna.exec();
  26.  
  27. }

信号量最著名的就是生产者与消费者的例子,以后再研究了。

QWaitCondition

QWaitCondition类提供了一个条件变量,它允许我们通知其他线程,等待的某些条件已经满足。等待QWaitCondition变量的可以是一个或多个线程。当我们用wakeOne()通知其他线程时,系统会随机的选中一个等待进行唤醒,让它继续运行。其实前面的信号量和读写锁内部实现都有用到QWaitCondition的。

下面我们来看这个类重要的几个函数:

ool QWaitCondition::wait ( QMutex * mutex, unsigned long time =ULONG_MAX )

该函数对mutex解锁,然后等待。在调用这个函数之前,mutex必须是加锁状态。如果mutex没有加锁,则函数直接返回。如果mutex是可递归的,函数也直接返回。该函数对mutex解锁,然后等待,知道以下条件之一满足:

1.     另外的线程调用wakeOne()或 wakeAll(),则该函数会返回true。

2.     时间过了Time毫秒。如果time为ULONG_MAX(默认),则将会一直等待不会超时。如果超时则返回false。

bool QWaitCondition::wait ( QReadWriteLock * readWriteLock, unsigned long time =ULONG_MAX )

函数对readWriteLock解锁并等待条件变量。在调用这个函数之前,readWriteLock必须是加锁状态的。如果不是加锁状态,则函数立即返回。readWriteLock必须不能是递归加锁的,否则将不能正确的解锁。返回的满足条件跟上面的函数一样。

http://blog.csdn.net/hai200501019/article/details/9889123

Qt同步线程(QMutex QMutexLocker QReadWriteLock QSemaphore QWaitCondition )的更多相关文章

  1. Qt同步线程(比较清楚,而且QMutex QMutexLocker QReadWriteLock QSemaphore QWaitCondition 每个都有例子)

    Qt同步线程 我们知道,多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确.Qt提供了一些类来实现线程的同步,如QMutex,QMute ...

  2. Qt QThread 线程创建,线程同步,线程通信 实例

    1.  继承QThread, 实现run()方法, 即可创建线程. 2. 实例1 代码 myThread.h #ifndef MYTHREAD_H #define MYTHREAD_H #includ ...

  3. Qt 控制线程的顺序执行(使用QWaitCondition,并且线程类的run函数里记得加exec(),使得线程常驻)

    背景项目中用到多线程,对线程的执行顺序有要求: A.一个线程先收数据 B.一个线程处理数据 C.一个线程再将处理后的数据发送出去 要求三个线程按照ABC的顺序循环执行. 思路子类化多线程方法 重写子类 ...

  4. Qt经典—线程、事件与Qobject(耳目一新)

    介绍 You’re doing it wrong. — Bradley T. Hughes 线程是qt channel里最流行的讨论话题之一.许多人加入了讨论并询问如何解决他们在运行跨线程编程时所遇到 ...

  5. Qt经典—线程、事件与Qobject

    介绍 You’re doing it wrong. — Bradley T. Hughes 线程是qt channel里最流行的讨论话题之一.许多人加入了讨论并询问如何解决他们在运行跨线程编程时所遇到 ...

  6. Linux多线程——使用互斥量同步线程

    前文再续,书接上一回,在上一篇文章: Linux多线程——使用信号量同步线程中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两 ...

  7. [转]QT子线程与主线程的信号槽通信-亲测可用!

    近用QT做一个服务器,众所周知,QT的主线程必须保持畅通,才能刷新UI.所以,网络通信端采用新开线程的方式.在涉及到使用子线程更新Ui上的控件时遇到了点儿麻烦.网上提供了很多同一线程不同类间采用信号槽 ...

  8. Qt 的线程与事件循环

    Qt 的线程与事件循环

  9. 线程:主线程、子线程 同步线程、异步线程 单线程、多线程 System.Threading与System.Windows.Threading

    入门-------------------------------------------------------------------------------- 概述与概念    一个C#程序开始 ...

随机推荐

  1. docker深入学习一

    docker是一个客户服务器结构的应用程序,其结构如下所示 其组成部分包括 container容器:是image的运行实例,一般container之间以及container与主机之间是相互隔离的,相当 ...

  2. json转义问题

    后端程序接受前台传递过来json 1正常json没有问题 比如  {"id":21,"userName":"2张天师","phon ...

  3. nginx配置比较杂乱的总结

    常用变量 demo uri www.example.com/mock/interface?param1=203&param2=test $args uri中的参数值 ?后面的部分 param1 ...

  4. 状态机的Verilog写法

    “硬件设计很讲究并行设计思想,虽然用Verilog描述的电路大都是并行实现的,但是对于实际的工程应用,往往需要让硬件来实现一些具有一定顺序的工作,这就要用到状态机思想.什么是状态机呢?简单的说,就是通 ...

  5. 如何访问Pod

    本章看点: 理清Deployment,ReplicaSet和Pod的关系,以及三者之间的网络关系,ip地址和端口号 通过Pod进入docker容器修改里面的内容 外部网络访问Pod里面的应用 一.通过 ...

  6. c# 操作xml文件(读写)

    根据项目需求,我们需要记录用户的操作痕迹,当用户下次登录操作同一个文件时,页面显示为用户上一次执行的用户轨迹,我们考虑把用户的历史记录写进xml文件中. 存储的xml数据结构: XML操作类: usi ...

  7. Alfred神器使用手册【转】

    我曾经耗费巨大的精力,试图在计算机的使用效率上找到一条优化的捷径,一直以来都收效甚微.直到遇上 alfred,它强大的工作流机制,彻底解决了输入输出的痛点,极大的减少了程序之间的切换成本和重复按键成本 ...

  8. 解决点击空<a>标签返回页面顶部的问题

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. C# 利用特性(Attribute)实现通用实体类数据合法校验

    用过asp.net mvc 的都应该知道,在实体类上添加一些特性,可以实现后端实体的数据校验,这里简单实现一下 实现原理:利用反射获取实体的每一个属性,并通过属性获取属性上标注的特性,调用特性的Val ...

  10. Flink源码阅读(一)——Flink on Yarn的Per-job模式源码简析

    一.前言 个人感觉学习Flink其实最不应该错过的博文是Flink社区的博文系列,里面的文章是不会让人失望的.强烈安利:https://ververica.cn/developers-resource ...