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 }
Одна из проблем применения мьютексов возникает из-за доступности переменной только для одного потока. В программах со многими потоками, пытающимися одновременно читать одну и ту же переменную (не модифицируя ее), мьютекс может серьезно снижать производительность. В этих случаях мы можем использовать
В классе
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 }
Ради удобства мы можем использовать классы
Класс
• QSemaphore semaphore(1) — QMutex mutex,
• Semaphore.acquire() — mutex.lock(),
• Semaphore.release() — mutex.unlock().
Передавая 1 конструктору, мы указываем семафору на то, что он управляет работой одного ресурса. Преимущество применения семафора заключается в том, что мы можем передавать конструктору числа, отличные от 1, и затем вызывать функцию
Типичная область применения семафоров — это передача некоторого количества данных
const int DataSize = 100000;
const int BufferSize = 4096;
char buffer[BufferSize];
Поток, являющийся поставщиком данных, записывает данные в буфер, пока он не заполнится, и затем повторяет эту процедуру сначала, переписывая существующие данные. Поток, принимающий данные, считывает данные по мере их поступления. Это проиллюстрировано на рис. 18.2 для небольшого 16-байтового буфера.
Необходимость синхронизации для примера взаимодействия потоков, один из которых формирует данные, а другой их считывает, обусловлена двумя причинами: если формирующий данные поток работает слишком быстро, он станет переписывать данные, которые еще не считал поток—приемник; если поток—приемник считывает данные слишком быстро, он перегонит другой поток и станет считывать «мусор».
Грубый способ решения этой проблемы состоит в том, чтобы сначала заполнить буфер и затем ждать, пока поток—приемник не считает буфер целиком и так далее. Однако в многопроцессорных системах это не позволит обоим потокам работать одновременно с разными частями буфера.
Одни из эффективных способов решения этой проблемы заключается в использовании двух семафоров:
QSemaphore freeSpace(BufferSize);
QSemaphore usedSpace(0);