Чтение онлайн

ЖАНРЫ

QT 4: программирование GUI на С++

Саммерфилд Марк

Шрифт:

Если при выполнении приложения вы нажмете кнопку Start А, консоль заполнится буквами «А». Если вы нажмете кнопку Start В, консоль заполнится попеременно последовательностями букв «А» и «В». Нажмите кнопку Stop А, и тогда на экран будет выводиться только последовательность букв «В».

Синхронизация потоков

Обычным требованием для многопоточных приложений является синхронизация работы нескольких потоков. Для этого в Qt предусмотрены следующие классы: QMutex, QReadWriteLock, QSemaphore и QWaitCondition.

Класс QMutex обеспечивает такую защиту переменной или участка

программного кода, что доступ к ним в каждый момент времени может осуществлять только один поток. Этот класс содержит функцию lock, которая закрывает мьютекс (mutex). Если мьютекс открыт, текущий поток захватывает его и немедленно закрывает; в противном случае работа текущего потока блокируется до тех пор, пока захвативший мьютекс поток не освободит его. В любом случае после вызова lock текущий поток будет держать мьютекс до вызова им функции unlock. Класс QMutex содержит также функцию tryLock, которая сразу же возвращает управление, если мьютекс уже закрыт.

Предположим, что нам нужно обеспечить защиту переменной stopped класса Thread из предыдущего раздела с помощью QMutex. Тогда мы бы добавили к классу Thread следующую переменную—член:

private:

QMutex mutex;

};

Функция run изменилась бы следующим образом:

01 void Thread::run

02 {

03 forever {

04 mutex.lock;

05 if (stopped) {

06 stopped = false;

07 mutex.unlock;

08 break;

09 }

10 mutex.unlock;

11 cerr << qPrintable(messageStr.ascii);

12 }

13 cerr << endl;

14 }

Функция stop стала бы такой:

01 void Thread::stop

02 {

03 mutex.lock;

04 stopped = true;

05 mutex.unlock;

06 }

Блокировка и разблокировка мьютекса в сложных функциях или там, где обрабатываются исключения С++, может иметь ошибки. Qt предлагает удобный класс QMutexLocker, упрощающий обработку мьютексов. Конструктор QMutexLocker принимает в качестве аргумента объект QMutex и блокирует его. Деструктор QMutexLocker разблокирует мьютекс. Например, мы могли бы приведенные выше функции run и stop переписать следующим образом:

01 void Thread::run

02 {

03 forever {

04 {

05 QMutexLocker locker(&mutex);

06 if (stopped) {

07 stopped = false;

08 break;

09 }

10 }

11 cerr << qPrintable(messageStr);

12 }

13 cerr << endl;

14 }

15 void Thread::stop

16 {

17 QMutexLocker locker(&mutex);

18 stopped = true;

18 }

Одна

из проблем применения мьютексов возникает из-за доступности переменной только для одного потока. В программах со многими потоками, пытающимися одновременно читать одну и ту же переменную (не модифицируя ее), мьютекс может серьезно снижать производительность. В этих случаях мы можем использовать QReadWriteLock — класс синхронизации, допускающий одновременный доступ для чтения без снижения производительности.

В классе Thread не имеет смысла заменять мьютекс QMutex блокировкой QReadWriteLock для защиты переменной stopped, потому что в лучшем случае только один поток может пытаться читать эту переменную в любой момент времени. Более подходящий пример мог бы состоять из одного или нескольких считывающих потоков, получающих доступ к некоторым совместно используемым данным, и одного или нескольких записывающих потоков, модифицирующих данные. Например:

01 MyData data;

02 QReadWriteLock lock;

03 void ReaderThread::run

04 {

05 …

06 lock.lockForRead;

07 access_data_without_modifying_it(&data);

08 lock.unlock;

09 …

10 }

11 void WriterThread::run

12 {

13 …

14 lock.lockForWrite;

15 modify_data(&data);

16 lock.unlock;

17 …

18 }

Ради удобства мы можем использовать классы QReadLocker и QWriteLocker для блокировки и разблокировки объекта QReadWriteLock.

Класс QSemaphore — это еще одно обобщение мьютекса, но, в отличие от блокировок чтения/записи, он может использоваться для контроля некоторого количества идентичных ресурсов. Следующие два фрагмента программного кода демонстрируют соответствие между QSemaphore и QMutex:

• QSemaphore semaphore(1) — QMutex mutex,

• Semaphore.acquire — mutex.lock,

• Semaphore.release — mutex.unlock.

Передавая 1 конструктору, мы указываем семафору на то, что он управляет работой одного ресурса. Преимущество применения семафора заключается в том, что мы можем передавать конструктору числа, отличные от 1, и затем вызывать функцию acquire несколько раз для захвата многих ресурсов.

Типичная область применения семафоров — это передача некоторого количества данных (DataSize) при совместном использовании циклического буфера определенного размера (BufferSize):

Поделиться с друзьями: