Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: lolbla2 от Март 29, 2012, 12:45



Название: Помогите понять как работают мьютексы в Qt.
Отправлено: lolbla2 от Март 29, 2012, 12:45
допустим есть разделяемый объект

Код
C++ (Qt)
QBuffer buf;

Есть общий мьютекс
Код
QMutex m;
есть классы:

Код
C++ (Qt)
class Th1: public QThread
{
public:    
void run();
}
void Th1::run()
{
    m.lock();
    buf.append("Th1");
    m.unlock();
    exec();
}
 
class Th2: public QThread
{
public:    
void run();
}
void Th2::run()
{
    m.lock();
    buf.append("Th2");
    m.unlock();
    exec();
}
 
Th1 th1;
Th2 th2;
th1.start();
th2.start();
 

Так вот имеются вопросы:

1)когда th1 запустил он сделал lock() на мьютекс, а в этом время th2 уснёт что ли или что с ним вообще будет пока мьютекс заблочен?
2) Они оба пытаются заблокировать мьютекс, получается заблочит его тот кто быстрее успеет это сделать?




Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: sudo от Март 29, 2012, 12:47
Кто первый встал, того и тапки. Один заблокировал, второй будет стоять и ждать, пока мьютекс разблокируется.

Есть ещё удобная штука - QMutexLocker, конструктор принимает указатель на мьютекс и сразу его блокирует, а в деструкторе разблокирует


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: mutineer от Март 29, 2012, 12:48
1) th2 будет сидеть в m.lock() и ждать пока мьютекс будет доступен


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: lolbla2 от Март 29, 2012, 12:53
1) th2 будет сидеть в m.lock() и ждать пока мьютекс будет доступен

то есть он уснёт пока, мьютекс не разблочат? Только не пойму что значит будет сидеть в m.lock(), то есть th2 выполнит m.lock() или нет?
Значит вроде я всё правильно понимаю.

а если сделать так

Код
C++ (Qt)
QMutex m1;
QMutex m2;
class Th1: public QThread
{
public:    
void run();
}
void Th1::run()
{
    m1.lock();
    buf.append("Th1");
    m1.unlock();
    exec();
}
 
class Th2: public QThread
{
public:    
void run();
}
void Th2::run()
{
    m2.lock();
    buf.append("Th2");
    m2.unlock();
    exec();
}
 
Th1 th1;
Th2 th2;
th1.start();
th2.start();

Тогда какое будет поведение у потоков?


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: mutineer от Март 29, 2012, 12:54
Отработают параллельно, возможно испортив buf


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: lolbla2 от Март 29, 2012, 13:00
Отработают параллельно, возможно испортив buf

Можно поподробнее 1 случай:
1. th1: m.lock() - выполнил th2: дошёл до строчки m.lock() не выполнил её и уснул, т.к. оказалось что мьютекс уже заблочен
2.th1: buf.append("Th1"); добавил в буффер, пошёл на m.unlock() - разблочил мьютекс, запустил цикл обработки событий. th2: спит, как только th1 сделал m.unlock() так сразу th2 заблочил мьютекс m.lock() добавил в буффер сообщение и далее m.unlock() - разблочил мьютекс и запустил свой цикл обработки событий.

Всё верно? если не так поправьте меня)


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: mutineer от Март 29, 2012, 13:01
В принципе да, все правильно


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 29, 2012, 13:56
Можно поподробнее 1 случай:
1. th1: m.lock() - выполнил th2: дошёл до строчки m.lock() не выполнил её и уснул, т.к. оказалось что мьютекс уже заблочен
2.th1: buf.append("Th1"); добавил в буффер, пошёл на m.unlock() - разблочил мьютекс, запустил цикл обработки событий. th2: спит, как только th1 сделал m.unlock() так сразу th2 заблочил мьютекс m.lock() добавил в буффер сообщение и далее m.unlock() - разблочил мьютекс и запустил свой цикл обработки событий.

Всё верно? если не так поправьте меня)
Верно, + важно понимать: во времени операции из разных ниток th1/th2 выполняются в ЛЮБОМ порядке. Общий мутекс гарантирует только что одна (и только одна) нитка будет выполнять защищенный участок. Др предположения неверны: напр запустили первой th1. потом th2, но оказывается th2 первой захватила мутекс. Или напр th1 первой добавила в буфер, но th2 первой запустила свой exec и получила сигнал

Ну и вообще-то мутексы везде так работают, не только в Qt


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: lolbla2 от Март 29, 2012, 14:08
Можно поподробнее 1 случай:
1. th1: m.lock() - выполнил th2: дошёл до строчки m.lock() не выполнил её и уснул, т.к. оказалось что мьютекс уже заблочен
2.th1: buf.append("Th1"); добавил в буффер, пошёл на m.unlock() - разблочил мьютекс, запустил цикл обработки событий. th2: спит, как только th1 сделал m.unlock() так сразу th2 заблочил мьютекс m.lock() добавил в буффер сообщение и далее m.unlock() - разблочил мьютекс и запустил свой цикл обработки событий.

Всё верно? если не так поправьте меня)
Верно, + важно понимать: во времени операции из разных ниток th1/th2 выполняются в ЛЮБОМ порядке. Общий мутекс гарантирует только что одна (и только одна) нитка будет выполнять защищенный участок. Др предположения неверны: напр запустили первой th1. потом th2, но оказывается th2 первой захватила мутекс. Или напр th1 первой добавила в буфер, но th2 первой запустила свой exec и получила сигнал

Ну и вообще-то мутексы везде так работают, не только в Qt

Хочешь сказать, что если нитка th1 первой добавила сообщение, но нитка th2 может быстрее запустить свой цикл событий, так как ей больше процессорного времени досталось?


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 29, 2012, 14:22
Да. Они не зависят друг от друга.

Т.е.  не рассчитывай на последовательное изменение ;) скорее на хаотичное :D


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 29, 2012, 14:34
Хочешь сказать, что если ..
Ну если Вы такой резвый, то вот Вам задачка

- сделать так чтобы нитки th1 и th2 (стартовавшие в любом порядке) добавили в буфер данные в строго определенном порядке - сначала th1, потом th2

А то бомбить вопросами легко  :)


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 29, 2012, 14:40
Хм и меня заинтересовал вопрос :)

Взаимо********, пара***** сигн****?


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: lolbla2 от Март 29, 2012, 16:05
Хочешь сказать, что если ..
Ну если Вы такой резвый, то вот Вам задачка

- сделать так чтобы нитки th1 и th2 (стартовавшие в любом порядке) добавили в буфер данные в строго определенном порядке - сначала th1, потом th2

А то бомбить вопросами легко  :)

Через QWaitCondition можно сделать думаю.


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 29, 2012, 16:22
Через QWaitCondition можно сделать думаю.
Ну так делайте  :)

Примечание: использование QWaitCondition и QSemaphore часто равноценно. Мне лично логика семафора нравится гораздо больше, вероятно просто потому что это был первый примитив который я освоил (у другого может быть наоборот).


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 29, 2012, 16:56
Агась. :D я WaitCondition освоил первым :D


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: BRE от Март 29, 2012, 17:04
Примечание: использование QWaitCondition и QSemaphore часто равноценно.
К сожалению нет.
При использовании условных переменных ядро разбудит одну из нитей (ожидающих эту переменную) вне очереди, а при семафорах нитка проснется и продолжит выполнение, только дождавшись своей очереди.


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 29, 2012, 17:13
К сожалению нет.
При использовании условных переменных ядро разбудит одну из нитей (ожидающих эту переменную) вне очереди, а при семафорах нитка проснется и продолжит выполнение, только дождавшись своей очереди.
Несколько ниток ждут на одном семафоре. Кто-то просигналил, семафор открылся, и одна из ниток его захватила. Чем здесь WaitCondition выгоднее?


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: BRE от Март 29, 2012, 17:26
У процесса 10 ниток. Пусть они выполняются в порядке их номеров.
Нитки 1, 2, 3 ждут на условной переменной, нитка 4 делает wakeOne, ядро может разбудить одну из ниток 1-3.
При семафоре: сначала отработают нитки 5-10, и только потом нитка 1 получит управление и захватит семафор.


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 29, 2012, 18:27
У процесса 10 ниток. Пусть они выполняются в порядке их номеров.
Нитки 1, 2, 3 ждут на условной переменной, нитка 4 делает wakeOne, ядро может разбудить одну из ниток 1-3.
При семафоре: сначала отработают нитки 5-10, и только потом нитка 1 получит управление и захватит семафор.
Т.е. нитка(и) ждущая на семафоре проснется только тогда когда ОС выделит ей квант времени? А на условной переменной нет? Никогда не слышал о таком, дайте линк. Спасибо

Также уточните о чем Вы говорите: о системных вызовах sem_wait и pthread_cond_wait или как? Потому что в Qt оба реализованы на pthread_cond_wait, и с этой точки зрения разницы нет.

Спускаясь на землю - семафор примитивнее, но часто именно эта простота и нужна. Число побудок равно числу просыпающихся - и все дела. Идеально для создатель-потребитель. WaitCondition хитрее, побудка может и не иметь эффекта. Но зато надо думать "хз а не проскочит ли". Зачем усложнять когда семафор делает все что нужно?


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: BRE от Март 29, 2012, 19:20
Т.е. нитка(и) ждущая на семафоре проснется только тогда когда ОС выделит ей квант времени? А на условной переменной нет? Никогда не слышал о таком, дайте линк. Спасибо
А ты поищи сам.
Я не все читаю в интернете и не всегда могу точно вспомнить источник. Это могла быть печатная статья, книга или исходники ядра linux.

Также уточните о чем Вы говорите: о системных вызовах sem_wait и pthread_cond_wait или как? Потому что в Qt оба реализованы на pthread_cond_wait, и с этой точки зрения разницы нет.
Я говорил про pthread_cond_xxx и sem_xx. Если в Qt и QWaitCondition и QSemaphore выполнен на условных переменных, то разницы конечно нет.

Но зато надо думать "хз а не проскочит ли".
Кто и куда не проскочит?


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 29, 2012, 20:29
Кто и куда не проскочит?
Вот пример из букваря (только выкинул sleep)
Код
C++ (Qt)
// "потребитель"
forever {
    mutex.lock();
    keyPressed.wait(&mutex);
    ++count;
    mutex.unlock();
 
    do_something();
 
    mutex.lock();
    --count;
    mutex.unlock();
}
 
// производитель
forever {
    getchar();
 
    mutex.lock();
    keyPressed.wakeAll();
    mutex.unlock();
}
Пусть потребитель увлечен тем do_something, на кеуpressed (он же condition) никто не ждет. Значит эффект wakeAll нулевой. Шустрый пользователь успевает ввести еще getchar - вот и "проскочило"

Кроме того, я лично недопонимаю один момент. С точки зрения "ожидающего побудки" все ясно
Код
C++ (Qt)
    mutex.lock();             // захватил мутекс
    keyPressed.wait(&mutex);  // освобождаю и жду
    ++count;                // здесь если я получил управление, то я же и владею мутексом
 

А вот что с будящим....
Код
C++ (Qt)
   mutex.lock();     // захватил мутекс
   keyPressed.wakeOne();   // бужу/будю
   mutex.unlock();        // так чей же мутекс??? Мой или того кто проснулся?
 

Объясните. Спасибо


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 29, 2012, 20:46
Помоему ваш пример насчёт нажатия кнопки ээээ... Как бы сказать - высосан из пальца.


Насчёт мутексов / разблокировки - я твой пример не понимаю.
Если разные потоки, то это будут разные мутексы. И ваше wakeAll не будет иметь эффекта.

Если один поток, то там (в простейшей ситуации) будет 1 мьютекс, который собственно лочится/анлочится.


Если честно не понимаю вообще вашего примера. WaitCondition ждёт пробуждения. Функция do_something() не должна являться рекурсивной, иначе теряется смысл WaitCondition.

WakeCondition (по моему мнению) идеальный вариант сигнала обработки данных.

Засыпаем.
     Данные добавляются.
Побудка.
    Просыпаемся. Обрабатываем всю пришедшую информацию.
Засыпаем.

А насчёт использовать его аналогично обычному сигналу (тот же keyPressed) помоему извращение. Как Семафор в качестве переменной int к примеру ;)


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: BRE от Март 29, 2012, 22:22
Пусть потребитель увлечен тем do_something, на кеуpressed (он же condition) никто не ждет. Значит эффект wakeAll нулевой. Шустрый пользователь успевает ввести еще getchar - вот и "проскочило"
Так очереди уже изобрели.
 
так чей же мутекс??? Мой или того кто проснулся?
Еще никто не проснулся, нитка с wait отметилась как готовая к исполнению, но она не проснется пока ты не освободишь мьютекс.



Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 30, 2012, 00:47
Помоему ваш пример насчёт нажатия кнопки ээээ... Как бы сказать - высосан из пальца.
Насчёт мутексов / разблокировки - я твой пример не понимаю.
..
Если честно не понимаю вообще вашего примера.
Как уже говорилось, это пример их букваря (Assistant)

WakeCondition (по моему мнению) идеальный вариант сигнала обработки данных.
Засыпаем.
     Данные добавляются.
Побудка.
    Просыпаемся. Обрабатываем всю пришедшую информацию.
Засыпаем.
Это как раз сценарий семафора  - мы всегда можем спокойно положиться на него. В случае waitCondition надо считаться с тем что побудка может прийти когда мы заняты обработкой. Это может быть и выгоднее
Код
C++ (Qt)
// обработчик
while (true) {
mutex.lock();   // захватили мутекс - будить никто не сможет (пока)
 
if (!data.size())    // если данных еще нет - освобождаем мутекс и ждем побудки
 myCondition.wait(&mutex);
 
// теперь мутекс опять наш
int val = data.takeAt(0);  // отхватили кусок данных под защитой
mutex.unlock();   // освободили данные
 
DoCalc(val);  // обрабатываем
}  
 


Еще никто не проснулся, нитка с wait отметилась как готовая к исполнению, но она не проснется пока ты не освободишь мьютекс.
Ну да, по-другому не видно как. Но смущает что в манах это "не звучит"   :)


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 30, 2012, 07:02
Я возможно невнимателен, возможно просто инитуитивно использую его именно в этом ключе. Как ожидающий поступления данных, с возможностью "проскакивания".

Причём в моих случаях, WaitCondition выгоднее семафора и "проще".

PS хотя кто знает, мб я его использую не так, как задумывали разработчики,но "это работает".


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 30, 2012, 13:30
Причём в моих случаях, WaitCondition выгоднее семафора и "проще".
Как и наоборот  :)  Вот задачка "проще некуда" - одна нитка читает консольный символ, другая печатает просто "pressed". Реализация любителя семафоров
Код
C++ (Qt)
// читатель
whle (true) {
getchar();    // ждем символа
theSemaphore.release(); // открываем семафор
}
 
// писвтель
whle (true) {
theSemaphore.acquire();    // ждем на семафоре
printf("pressed");  // печатаем
}
 
А как это сделать на QWaitCondition ?


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 30, 2012, 14:15
В приведённом вами примере, нет необходимости в ожидании. Семафор там используется просто как посыл сигнала.


Скажем проще - в ситуации:
Неизвестно когда/что/кто/сколько добавит и надо обработать - waitCondition полезно. (обработка всех добавленных данных и опять спячка)

А когда:
Ожидаем действия пользователя и вызываем адекватную реакцию - тут уже семафоры/сигналы.

WaitCondition не позиционируется как сигнал/система реального времени (мб перемудрил с определением). Он не даст адекватной быстрой реакции в ваших примерах.

А вот в случае аля "слушаем 500 устройств, возможно кто-то что-то пришлёт, а присланное и разобрать надо неизвестной длины" - тогда уже wait рулит ;)

PS возможно их использование и как аналогов, но они разного принципа ;)


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 30, 2012, 14:41
Реализация с семафором проста и понятна - по 2 строчки на нитку. А что с WaitCondition? С высказанными общими соображениями я вполне согласен, ну а делать-то что? Приводим псевдокод или признаем что такую мелочь оказывается сложно сделать на  condition  :)


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: BRE от Март 30, 2012, 15:27
Реализация с семафором проста и понятна - по 2 строчки на нитку. А что с WaitCondition? С высказанными общими соображениями я вполне согласен, ну а делать-то что? Приводим псевдокод или признаем что такую мелочь оказывается сложно сделать на  condition  :)
Ну если QSemaphore сделан на QWaitCondition, то можно развернуть методы семафора. :)

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


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 30, 2012, 17:40
Igors,  вы мне предлагаете выложить вам вариант костыля использования QWaitCondition в роли семафора. :)

А выложите-ка вы мне сначала пример реализации "проскакивающего" пробуждения (вы ведь ранее его сами приводили и недоумевали), используя Семафор заместо WaitCondition.

PS вот сейчас вы конкретно неправильно ставите вопрос. Это разные классы с почти одинаковыми возможностями. Но только почти.



Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 30, 2012, 18:03
Igors,  вы мне предлагаете выложить вам вариант костыля использования QWaitCondition в роли семафора. :)
А чего это Вы мне так сразу поверили что здесь семафор якобы лучше? :)  QWaitCondition ничем не хуже, ну надо написать по 4 строчки на нитку вместо 2, чего торгуетесь? :) Если у Вас трудности - я напишу, не вопрос. А не надо - ну так и проехали.


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Bepec от Март 30, 2012, 18:04
Таки я ответил на ваш вопрос ещё раньше - почитайте мои предыдущие сообщения. WaitCondition ждёт / отрабатывает / засыпает.


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: Igors от Март 30, 2012, 19:04
Посмотрел что сейчас делается на этом фронте...
Для систем с многоядерными процессорами есть один момент. Условная переменная имеет возможность подготовить к пробуждению одну нитку из нескольких ждущих, а с семафорами похуже. Ядро готовит к пробуждению все нитки ждущие на семафоре, что бы они в конкурентной борьбе могли захватить ресурс. Т.е. ждут пять ниток, появился один ресурс, все проснулись, самая быстрая его захватили, все остальные проверили и заснули дальше.
Интересно, где читаете?

Неясно конечно в чем заключается та "подготовка к пробуждению". И вот с "одной" - ведь в ОC wake будит "хотя бы одну", но может и больше, какую тогда готовит? Понятно не все вопросы имеют (простые) ответы.


Название: Re: Помогите понять как работают мьютексы в Qt.
Отправлено: BRE от Март 30, 2012, 20:06
Неясно конечно в чем заключается та "подготовка к пробуждению".
Изменение состояния нитки - флаг поменять. Ядро для каждого объекта синхронизации хранит очередь ниток, которые ожидают освобождения этого объекта. Все они имеют состояние останова. Что бы пробудить нужно это состояние поменять.

И вот с "одной" - ведь в ОC wake будит "хотя бы одну", но может и больше, какую тогда готовит? Понятно не все вопросы имеют (простые) ответы.
С условными переменными, я как разработчик, могу сказать ядру разбуди одну нить (wakeOne) не важно какую или разбудить все ожидающие нити (wakeAll).
Если я добавил одно задание в очередь заданий, то мне не нужно будить все ожидающие нити, мне будет достаточно одной. А если мне нужно, что бы все нитки завершились, то я устанавливаю флаг и прошу проснутся всех. У семафоров такой возможности нет.