Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: niktagor от Сентябрь 22, 2010, 16:41



Название: QMutex
Отправлено: niktagor от Сентябрь 22, 2010, 16:41
 В документации написано:

QMutex::Recursive In this mode, a thread can lock the same mutex multiple times and the mutex won't be unlocked until a corresponding number of unlock() calls have been made.

QMutex::NonRecursive In this mode, a thread may only lock a mutex once.

Если у меня один QMutex на несколько потоков, в нерекурсивном режиме каждый поток сможет сделать ему свой lock()? Есть ли какие-нибудь гарантии, что они получят к нему доступ в том порядке, в котором они этот lock() вызвали?


Название: Re: QMutex
Отправлено: Авварон от Сентябрь 22, 2010, 17:22
мьютекс может быть заблокирован только один РАЗ (в случае рекурсивного лока, это не приведет к зависанию, если поток 1 и тот же локает несколько раз)


Название: Re: QMutex
Отправлено: Igors от Сентябрь 22, 2010, 19:23
В документации написано:

QMutex::Recursive In this mode, a thread can lock the same mutex multiple times and the mutex won't be unlocked until a corresponding number of unlock() calls have been made.

QMutex::NonRecursive In this mode, a thread may only lock a mutex once.

Если у меня один QMutex на несколько потоков, в нерекурсивном режиме каждый поток сможет сделать ему свой lock()? Есть ли какие-нибудь гарантии, что они получят к нему доступ в том порядке, в котором они этот lock() вызвали?
Абсолютно никаких, порядок не определен, это "базовая" установка/понятие. Вызвавший lock позже может захватить его (а не тот кто вызвал lock раньше).

Рекурсивный мутекc - в природе его нет, это просто удобная замена такого:
Код
C++ (Qt)
static QT::HANDLE theOwner = 0;
static QMutex theMutex;
 
void DoSomethingRecursive( void )
{
// захватываем мутекс если он еще не захвачен текущей ниткой
 bool doRelease = false;
 Qt::HANDLE curID = QThread::currentThreadId();
 if (curId != theOwner) {
   theMutex.lock();
   theOwner = curId;
   doRelease = true;
 }
 
// считаем под защитой мутекса
 ...
 DoSomethingRecursive();  // возможен рекурс
 ...
 
// освобождаем для рекурсии уровня 0
 if (doRelease) {
   theOwner = 0;
   theMutex.unlock();
 }
}  
 
 


Название: Re: QMutex
Отправлено: niktagor от Сентябрь 22, 2010, 19:24
мьютекс может быть заблокирован только один РАЗ (в случае рекурсивного лока, это не приведет к зависанию, если поток 1 и тот же локает несколько раз)
А если разные потоки локают?
 Мне нужно, чтобы разные потоки могли читать/записывать в одну переменную. Причем каждый из них должен ждать освобождения переменной и ничего не делать в это время. Как лучше реализовать?


Название: Re: QMutex
Отправлено: Kolobok от Сентябрь 22, 2010, 19:32
А если разные потоки локают?
 Мне нужно, чтобы разные потоки могли читать/записывать в одну переменную. Причем каждый из них должен ждать освобождения переменной и ничего не делать в это время. Как лучше реализовать?

Использовать QMutex. Он для этого и служит.


Название: Re: QMutex
Отправлено: Igors от Сентябрь 22, 2010, 19:35
А если разные потоки локают?
 Мне нужно, чтобы разные потоки могли читать/записывать в одну переменную. Причем каждый из них должен ждать освобождения переменной и ничего не делать в это время. Как лучше реализовать?
Смотря что за переменная. Если напр. надо класть/извлекать из контейнера, то просто делать это под защитой мутекса
Код
C++ (Qt)
theMutex.lock();
myList.push_back(newData);
theMutex.unlock();
 
В более сложных случаях надо заботиться о том, что прочитанные данные уже изменены


Название: Re: QMutex
Отправлено: niktagor от Сентябрь 22, 2010, 20:11
 В моей ситуации 10 процессов ждут доступа на чтение к одному очень большому массиву данных. А один процесс их довольно часто перезаписывает. И может возникнуть ситуация, когда пара процессов всегда будут тупо висеть в ожидании. Поэтому нужно реализовать очередность, чтобы первый вставший в очередь на считывание первым получил доступ. Как это лучше сделать?


Название: Re: QMutex
Отправлено: Авварон от Сентябрь 22, 2010, 20:28
никак, это задача "удушения"
можно играться с приоритетом потоков, но в общем случае считается, что все потоки равноправны и кто получит доступ, неизвестно


Название: Re: QMutex
Отправлено: Igors от Сентябрь 22, 2010, 20:39
В моей ситуации 10 процессов ждут доступа на чтение к одному очень большому массиву данных. А один процесс их довольно часто перезаписывает. И может возникнуть ситуация, когда пара процессов всегда будут тупо висеть в ожидании. Поэтому нужно реализовать очередность, чтобы первый вставший в очередь на считывание первым получил доступ. Как это лучше сделать?
Возможно с 10 читающими всего 1 пишущему трудно будет прорваться к записи, тогда надо использовать QReadWriteLock. По поводу того что, мол, какие-то нитки будут всегда стоять: распределением нагрузки занимается OC, так что "всегда" никто стоять не будет. Может быть что одна нитка выполнит 8 задач, а другая за это же время - всего 2. Это нормально, не надо стараться сделать 5/5 - скорость будет хуже.


Название: Re: QMutex
Отправлено: niktagor от Сентябрь 22, 2010, 20:43
 Не может быть, чтобы не было решения. Пусть очередь будет сколь угодно большая. Нужна гарантия, что каждый поток  будет продвигаться по ней только вперед, а не назад. Пусть не с помощью мутексов.  
 Первое, что пришло в голову:
Создаем поток, занимающийся только обслуживанием очередности. У него для каждого потока есть свой отдельный мутекс. Когда он видит, что данные изменились, он разблокирует мутекс нужного потока и тот устремляется к данным. Будет работать.
 Но должно же быть красивое решение!

Цитировать
Возможно с 10 читающими всего 1 пишущему трудно будет прорваться к записи, тогда надо использовать QReadWriteLock
Так и буду делать. Но нужно еще заботиться о том, чтобы все читающие иногда получали доступ. Пусть не после каждого изменения данных, но получали.


Название: Re: QMutex
Отправлено: Akon от Сентябрь 22, 2010, 20:45
2 niktagor:

QMutex mutex;
mutex.lock();
mutex.lock();  // Error

QMutex mutex(QMutex::Recursive);
mutex.lock();
mutex.lock();  // OK

Вся разница. Нерекурсивный мьютекс это частный случай рекурсивного.


Название: Re: QMutex
Отправлено: Igors от Сентябрь 22, 2010, 20:54
Но должно же быть красивое решение!
:) Мне нравится Ваш подход, но решение "чего"? Как уже сказали "все нитки нагружены одинаково" = недостижимо. Тогда чего Вы хотите добиться?

Пусть очередь будет сколь угодно большая. Нужна гарантия, что каждый поток  будет продвигаться по ней только вперед, а не назад.
Ну так это и будет само собой достигаться, зачем надо прилагать еще какие-то усилия?


Название: Re: QMutex
Отправлено: niktagor от Сентябрь 22, 2010, 21:04
Пусть очередь будет сколь угодно большая. Нужна гарантия, что каждый поток  будет продвигаться по ней только вперед, а не назад.
Ну так это и будет само собой достигаться, зачем надо прилагать еще какие-то усилия?

 Кажется, понял. Так и сделаю. Просто страшно пологаться в таком деле на Виндовс.


Название: Re: QMutex
Отправлено: Igors от Сентябрь 22, 2010, 21:30
QMutex mutex(QMutex::Recursive);
mutex.lock();
mutex.lock();  // OK

Вся разница. Нерекурсивный мьютекс это частный случай рекурсивного.
Разница в том что второй лок просто ничего не лочит  :)


Название: Re: QMutex
Отправлено: Akon от Сентябрь 23, 2010, 09:10
QMutex mutex(QMutex::Recursive);
mutex.lock();
mutex.lock();  // OK

Вся разница. Нерекурсивный мьютекс это частный случай рекурсивного.
Разница в том что второй лок просто ничего не лочит  :)

Лочит - на каждый лок нужен анлок.


Название: Re: QMutex
Отправлено: Igors от Сентябрь 23, 2010, 09:58
Лочит - на каждый лок нужен анлок.
Да, но второй и последующие lock/unlock просто сводятся к увеличению/уменьшению внутреннего счетчика QMutex, никаких вызовов ОС не происходит. Т.е. рекурсивный мутекс не является "чем-то особенным"


Название: Re: QMutex
Отправлено: Akon от Сентябрь 23, 2010, 10:31
Лочит - на каждый лок нужен анлок.
Да, но второй и последующие lock/unlock просто сводятся к увеличению/уменьшению внутреннего счетчика QMutex, никаких вызовов ОС не происходит. Т.е. рекурсивный мутекс не является "чем-то особенным"

Да не вопрос. Так и есть.