Russian Qt Forum
Ноябрь 23, 2024, 02:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 8   Вниз
  Печать  
Автор Тема: Использование QWaitCondition  (Прочитано 74675 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Декабрь 17, 2012, 22:30 »

Добрый день

На мой взгляд, QWaitCondition - вещь хитрая, и надеяться на то что "прочитал ассистент и сделал по образцу" не приходится. Часто слышно "используй QWaitCondition", но доходит до дела - и никто не знает как Улыбающийся  Ну так почему бы нам это не перетереть и не углУбить свои знания/понимание? Есть задачки на QWaitCondition - постите сюда, обсудим. Начнем с простого: вот пример неправильного использования. Я специально не привожу автора - все ошибаются, но дело не в том кто ошибся, а как правильно
Цитировать
т.е. строго по доке:

поток 1:

захват мутекса
wait
отпускаем мьютекс

поток 2:

wake
Ну насчет "строго по доке" - верить необязательно  Улыбающийся

Да, и не надо спрашивать "а в чем вопрос?" - не каждый пост должен быть вопросом  Улыбающийся
Записан
Bepec
Гость
« Ответ #1 : Декабрь 17, 2012, 23:16 »

Кхм. Мини вопрос - а что там неправильного?

Возможно моих знаний не хватает. Возможно мой проект на неправильном использовании сделан и работает. Так в чём?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Декабрь 18, 2012, 10:27 »

Кхм. Мини вопрос - а что там неправильного?
Этот псевдокод не ведет к крашу и "при каком-то стечении обстоятельств" может быть работоспособным. Он просто наивен. Вызывая wakeOne/wakeAll из нитки 2 мы почему-то считаем, что нитка 1 ждет на QWaitCondition. Если это так - все норм. Однако если нитка 2 по каким-то причинам не успела встать на wait - все рушится. wake не имеет эффекта т.к. нет ожидающих на мутексе. И когда позже нитка 2 пришла на wait - ее уже некому разбудить.

Возможно мой проект на неправильном использовании сделан и работает.
Предъявляем псевдокод или упрощенный проект (Вам виднее) - посмотрим, пощупаем
Записан
Bepec
Гость
« Ответ #3 : Декабрь 18, 2012, 13:37 »

вырезанный код
Код:
/*
    private:
       QWaitCondition wait;
       bool flagStop;
       QMutex lock;
*/
public:

void run
{
    for(;!flagStop;)
    {
        lock.lock();
        wait.wait(&lock);

        if (turn.size() == 0)
        {
            lock.unlock();
            continue;
        }
        int tmpSize = turn.size();
        lock.unlock();
        for(;tmpSize != 0;)
        {
            createResultString(getTurn());
            tmpSize--;
         }
    }
}

void addTurn(некие данные inData)
{
    lock.lock();
    turn.push(inData);
    lock.unlock();
    wait.wakeAll();
}

Поток запущен, ждёт поступления данных.
Другие потоки читают/генерируют данные, вызывают функцию addTurn.
Поток просыпается, обрабатывает данные до конца очереди и опять засыпает.
« Последнее редактирование: Декабрь 18, 2012, 15:31 от Bepec » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Декабрь 18, 2012, 14:14 »

вырезанный код
Код:
void run
{
    for(;!flagStop;)
    {
        lock.lock();
        wait.wait(&lock);

        for (;turn.size() != 0;)
        {
            // тут много много действий по обработке ;          
        }
    }
}
После выхода из wait.wait мутекс захвачен этой ниткой, поэтому уточните где Вы его освобождаете в run. Лучше исправьте/дополните Ваш пост, т.к. двоечтение и/или оговорку на словах обсуждать будет трудно
« Последнее редактирование: Декабрь 18, 2012, 14:17 от Igors » Записан
Bepec
Гость
« Ответ #5 : Декабрь 18, 2012, 15:32 »

Добавил в моё сообщение выше вырезку.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Декабрь 18, 2012, 16:39 »

Ладно, давайте я распишу с комментариями
Код
C++ (Qt)
void run
{
   while (true) {
   {
      if (flagStop) break;   // проверяем флаг останова
      lock.lock();              // захватываем лок, др нитки не смогут изменить turn пока не освободим
      if (!turn.size())        // если в очереди нет задач -
        wait.wait(&lock);    // wait освободил мутекс, ждем побудки
 
// после возврата из wait мы опять владеем мутексом
// должны извлечь задачи из oчереди (turn) и освободить мутекс поскорее
// сколько задач извлекать - дело хозяйское
      int numExtract = turn.siize();
      if (mProcessMode == process_Single)  
         numExtract = qMin(numExtract, 1);
 
// извлекаем во временный контейнер  
      Container temp;
      for (int i = 0; i < numExtract; ++i)
        temp.push_back(turn.takeAt(0));
 
// освобождаем мутекс
       lock.unlock();
 
// обрабатываем извлеченные данные
      ProcessData(temp);
   }
}
 
Ваш код имеет 2 помарки

- не нужно делать wait если задачи в очереди есть, так некоторые задачи могут долго стоять в очереди если она не пополняется

- как только мутекс освобожден Вы не должны трогать turn до след захвата
« Последнее редактирование: Декабрь 18, 2012, 16:41 от Igors » Записан
Bepec
Гость
« Ответ #7 : Декабрь 18, 2012, 16:55 »

Эм?

1)
Если задачи в очереди есть? Это как? Этот код начинает выполняться в момент запуска потока.
Потому де факто у него нет данных. В дальнейшем wait будет ждать до тех пор, пока не появятся данные.
Когда появятся данные, все они будут обработаны, т.е. при следующей проходке цикла у нас опять получается отсутствие данных.

И если же сделать проверку, то я думаю, можно вполне попасть в ситуацию, когда 1 поток будет добавлять данные, а поток тут же их обрабатывать, не оставляя возможности другим потокам. (смутно, но думаю смысл донёс).

2)
Расписываю. Получаю мутекс, получаю размер полученных данных. Отпускаю мутекс.

Далее циклично вызывается слот createResultString(getTurn()).

getTurn() имеет в своем теле конструкция аля
Код:
    lock.lock();
    // получаем элемент из очереди. разблокируем. обрабатываем.     
    pars tmp;
    tmp = turn.front();
    turn.pop();
    lock.unlock();
    return tmp;
.

имеют ли ваши помарки обоснования, при этой добавочной информации?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Декабрь 18, 2012, 17:39 »

имеют ли ваши помарки обоснования, при этой добавочной информации?
Да, имеют, правда помарки не мои а Ваши Улыбающийся Давайте по порядочку

1)
Если задачи в очереди есть? Это как? Этот код начинает выполняться в момент запуска потока.
Потому де факто у него нет данных. В дальнейшем wait будет ждать до тех пор, пока не появятся данные.
Когда появятся данные, все они будут обработаны, т.е. при следующей проходке цикла у нас опять получается отсутствие данных.
В жизни это происходит по-другому. Какое-то число wakeAll "съедается" т.к. нет ожидающих на мутексе. напр
Код:
        lock.unlock();
// В этот момент др нитка добавила новую задачу и сделала wakeAll которое  не имеет эффекта
        for(;tmpSize != 0;)
        {
Теперь представим себе что после этого задач (пока) не поступает. Тогда на след витке обработчик ждет на wait - хотя задача-то уже есть

Не спешите, здесь лучше разобраться капитально, вещь непростая
Записан
Bepec
Гость
« Ответ #9 : Декабрь 18, 2012, 17:56 »

И вправду слабое место тут есть, но к счастью не в моём проекте Веселый Имеется нитка, генерирующая постоянные события и данная технология используется для логгирования Улыбающийся

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

Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #10 : Декабрь 18, 2012, 18:18 »

Скорее всего не делать wakeAll Улыбающийся, а будить только те потоки, которые в этом нуждаются, не?

И вообще, какая используется "архитектура"? Один производитель и много потребителей?
« Последнее редактирование: Декабрь 18, 2012, 18:20 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Декабрь 18, 2012, 18:20 »

И каково же решение этой проблемы? Не отпускать мутекс, пока не будет обработано всё ?
Это плохо т.к. заставляет подающие нитки ждать конца обработки

(или помещено в отдельный контейнер?) не будет ли это затратнее чем постоянно генерящиеся события?
Реально очередь почти всегда - контейнер указателей на задачи, затраты на их перемещение пренебрежимо малы по сравнению с обработкой. В Qt может быть и не указатель, но с имплисит шарой тоже быстро. Вообще про временный контейнер я упомянул для полноты картины. Обработка "пачками" возможна, но встречается редко, действовать "по одному" проще, а тогда достаточно просто
Код
C++ (Qt)
temp = turn.takeAt(0);
lock.unlock();
ProcessData(temp);
 
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #12 : Декабрь 18, 2012, 18:28 »

Код
C++ (Qt)
temp = turn.takeAt(0);
lock.unlock();
ProcessData(temp);
 

А чем это в принципе отличается от:
Код
C++ (Qt)
// извлекаем во временный контейнер  
      Container temp;
      for (int i = 0; i < numExtract; ++i)
        temp.push_back(turn.takeAt(0));
 
// освобождаем мутекс
       lock.unlock();
 
// обрабатываем извлеченные данные
      ProcessData(temp);
 

?

В чем прикол то? Если for(i = 0...) тоже быстро выполнится, считаем, что затрат нет.
Записан

ArchLinux x86_64 / Win10 64 bit
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Декабрь 18, 2012, 18:31 »

Скорее всего не делать wakeAll Улыбающийся, а будить только те потоки, которые в этом нуждаются, не?
Сильная  сторона WaitCondition как раз в том что будиться будут спящие, а уже работающим та побудка пофиг

И вообще, какая используется "архитектура"? Один производитель и много потребителей?
Та откуда я знаю? Учил транзисторы и даже лампы (1 семестр), яка там "архитектура"

В чем прикол то? Если for(i = 0...) тоже быстро выполнится, считаем, что затрат нет.
Нет никакого прикола - просто хочу по-одному, а могу и пачками (разные варианты, суть одна)
« Последнее редактирование: Декабрь 18, 2012, 18:33 от Igors » Записан
Bepec
Гость
« Ответ #14 : Декабрь 18, 2012, 18:32 »

Много производителей и один потребитель Улыбающийся Производители - потоки, генерящие данные. Потребитель - один этот поток, преобразующий в необходимую форму лог.

В моём случае несущественно.

to Igors:
Но ведь wakeAll тогда в любом случае может "пропустить" цикл. Как от этого избавиться?

PS в любом случае, я так понимаю, если в ожидающем цикле будут действия после unlock, это приведёт как раз к возможному пропуску wakeAll(). так ведь?

update:
В том то всё и дело, что в любом случае после unlock у нас получается участок времени (пусть пренебрежительно малый, но всё же), во время которого наш wakeAll умрёт и в очереди останется необработанная часть данных.

И даже несколько потоков не дадут 100% гарантии в покрытии этого места.
Записан
Страниц: [1] 2 3 ... 8   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.13 секунд. Запросов: 23.