const int BufferSize = 4096;
char buffer[BufferSize];
QWaitCondition bufferIsNotFull;
QWaitCondition bufferIsNotEmpty;
QMutex mutex;
int usedSpace = 0;
Кроме буфера мы объявляем два объекта
01 void Producer::run()
02 {
03 for (int i = 0; i < DataSize; ++i) {
04 mutex.lock();
05 while (usedSpace == BufferSize)
06 bufferIsNotFull.wait(&mutex);
07 buffer[i % BufferSize] = "ACGT"[uint(rand()) % 4];
08 ++usedSpace;
09 bufferIsNotEmpty.wakeAll();
10 mutex.unlock();
11 }
12 }
Работу потока, формирующего данные, мы начинаем с проверки заполнения буфера. Если он заполнен, мы ждем возникновения условия «буфер не заполнен». Когда это условие удовлетворяется, мы записываем один байт в буфер, увеличиваем на единицу
Мы используем мьютекс для контроля любого доступа к переменной
В этом примере мы могли бы заменить цикл
while (usedSpace == BufferSize)
bufferIsNotFull.wait(&mutex);
на инструкцию
if (usedSpace == BufferSize) {
mutex.unlock();
bufferIsNotFull.wait();
mutex.lock();
}
Однако это не будет правильно работать, как только мы станем использовать несколько потоков, формирующих данные, поскольку другой такой поток может захватить мьютекс сразу же после вызова функции
01 void Consumer::run()
02 {
03 for (int i = 0; i < DataSize; ++i) {
04 mutex.lock();
05 while (usedSpace == 0)
06 bufferIsNotEmpty.wait(&mutex);
07 cerr << buffer[i % BufferSize];
08 --usedSpace;
09 bufferIsNotFull.wakeAll();
10 mutex.unlock();
11 }
12 cerr << endl;
13 }
Поток—приемник работает в точности наоборот относительно первого потока: он ожидает возникновения условия «буфер не пустой» и возобновляет работу любого потока, ожидающего условия «буфер не заполнен».
Во всех приводимых до сих пор примерах наши потоки имеют доступ к одинаковым глобальным переменным. Но для некоторых многопоточных приложений требуется хранить в глобальных переменных неодинаковые данные для разных потоков. Эти переменные часто называют локальной памятью потока (thread-local storage — TLS) или специальными данными потока (thread-specific data — TSD). Мы можем «схитрить» и использовать отображение, в качестве ключей которого применяются идентификаторы потоков (возвращаемые функцией
Обычно класс
01 QThreadStorage
02 void insertIntoCache(int id, double value)
03 {
04 if (!cache.hasLocalData())
05 cache.setLocalData(new QHash
06 cache.localData()->insert(id, value);
07 }
08 void removeFromCache(int id)
09 {
10 if (cache.hasLocalData())
11 cache.localData()->remove(id);
12 }