Предположим, что нам нужно обеспечить защиту переменной 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):

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже