Модераторы: bsa
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Синхронизация остановки\возобн. потока 
:(
    Опции темы
OpenMan
Дата 2.7.2010, 12:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 19.4.2009

Репутация: нет
Всего: 1



Задача такова:
Есть поток А, который делает что-то с данными.
И есть другие потоки Б, которые приносят новые данные. Потоки Б между собой синхронизированны на предмет приноса новых данных.

Как я понимаю, если есть данные то поток А должен работать, если этих данных нет, то поток должен спать до тех пор, пока
не появятся новые данные. Тока никак не пойму как это можно реализовать.


Поток Б.

  добавил данные;
  isNewData = true;
  // синхронизация +
  if(isSleep){
      возобновляем поток А
  }
  // синхронизация -

Поток А

while(true){
   if(isNewData){
        isNewData = false;
        действие с данными новыми
        continue;
   }
   else{
       // синхронизация +
       isSleep = true;
       // синхронизация -
       усыпляем поток А
      
       isSleep = false;
  }
}

прошу прощения за псевдокод и за то, что оформил не в рамке. 
Проблема вот в чем, возможна такая ситуация, когда поток А проверит и убедиться, что нет новых данных, установит флаг isSleep,
в это же время поток Б добавит новые данные, посмотрит, что поток А вырублен (isSleep==true), и постарается его возобновить,
а после этого поток А уснет. Получится, что с одной стороны поток вырублен, а с другой стороны флаг isSleep равен false.

по понятным причинам включить инструкцию "усыпляем поток А" внутрь синхронизированного блока не получиться.
Как быть?

Один из вариантов - это пытаться восcтановить поток вне зависимости от флага isSleep. Но в этом случае получиться громадная дополнительная нашрузка на ЦП, Ведь новые данные поступают много маленькими кусочками, и время на обработку каждого мало, а операция восстановления потока не быстрая (как я понимаю). Получиться, что 80% ЦП будет уходить на избыточную "возобновляем поток А".

Есть ещё вариант вместо приостанавливании потока используем Sleep(50) (на время); Но в этом случае возможен вариант, когда данные будут ждать 50 милсек, а мне надо как можно раньше данные обработать. Ставить Sleep(5) - опять же зря напрягать центральный процессор.

Как люди в таких ситуациях поступают? Чую должен быть какой-то паттерн на этот случай.

Добавлено через 8 минут и 30 секунд
Пардон, глупость сморозил.
С флагами все правильно... Но сама проблема остаётся. Новые данные могут быть добавлены, но после "восстановления потока А", может случиться "усыпляем поток А", и таким образом, данные буду не обработаны до прихода следующих данных, что не хорошо.
PM MAIL   Вверх
bsa
Дата 2.7.2010, 15:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



OpenMan, почитай про семафоры. Т.е. это такой тип переменной, которую один из потоков увеличивает, а другой уменьшает. Если она равна нулю, а ее попробует уменьшить второй, то он засыпает до тех пор, пока первый не увеличит.
PM   Вверх
OpenMan
Дата 2.7.2010, 18:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 19.4.2009

Репутация: нет
Всего: 1



Примерно я представляю, что это такое. Только как это может помочь?
Собственно, как я понимаю семафоры отличаются от критических секций только тем, что у критических секций может выполняться только 1 поток.

Фишка в том, что нужно, чтобы после того как поток Б выходит из своей зоны (оставил данные), поток А проснулся если спал. Уже минут 20 думаю. но не понимаю что тут могут дать семафоры.
PM MAIL   Вверх
OpenMan
Дата 2.7.2010, 19:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 19.4.2009

Репутация: нет
Всего: 1



Попробую поставить задачу более абстрактно.

Есть Пекать который печет булки используя муку. Условно из одного мешка получается 1 булка. И есть много слуг, которые приносят муку к пекарю.

Поведение пекаря:

если есть мука в наличии, то берем 1 мешок и делаем 1 булка, далее проверяем есть ли ещё мука и так далее.
Если после очередной работы пекарь не обнаруживает муку, то он ложится спать.

Поведение слуги.

пришел, положил мешок, если пекарь спит, то будим пекаря.

Сейчас я описал стандартную задачу "с буффером" и обрабатывающим этот буфер потоком. Как мне кажется такая задача встречается много где. В чем загвостка, а в том, что у пекаря " проверить склад муки и заснуть" - это 2 команды, и межу ними может много чего произойти.

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

Обычно подобные вещи решаются объединением 2-х команд в одну критическую секцию. Но по понятным причинам нельзя в такую секцию засунуть команду "спать", иначе поток заснет и секция будет занята, и никто не сможет разбудить пекаря.

Желательно, чтобы пекарь работал 
1) Оперативно,т.е. если мука есть, он должен в тот час же приступать к работе, в пределах времени требуемой на пробуждение.

2) Минимально, т.е. спал если муки нет. 
PM MAIL   Вверх
vnf
Дата 2.7.2010, 22:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 9.2.2008

Репутация: нет
Всего: 1



А почему используете sleep? Неподходяще.
Тут надо использовать функции ожидания и семафор (выше предлагали).


PM MAIL   Вверх
OpenMan
Дата 3.7.2010, 12:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 19.4.2009

Репутация: нет
Всего: 1



Можно шаблон того, как прикрутить сюда семафоры. В упор не вижу как с помощью них решить проблему.

Цитата

А почему используете sleep? Неподходяще.


Нет, я в основном использую wait() под словом спать. Не правильно выразился я.
PM MAIL   Вверх
bsa
Дата 3.7.2010, 19:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



OpenMan, вот смотри, пекарь уменьшает семафор, слуга увеличивает. в чем проблема? Все давно уже решено.
Код
пекарь()
{
   do {
      sem_wait(sem); //спать, пока нет мешков
      pthread_mutex_lock(mutex); //входим в критическую секцию взятия мешка
      взять мешок
      pthread_mutex_unlock(mutex); //выходим из критической секции взятия мешка
      печь булку
   } while(1);
}

слуга()
{
   do {
      готовить мешок
      pthread_mutex_lock(mutex); //входим в критическую секцию добавления мешка
      добавить мешок
      pthread_mutex_unlock(mutex); //выходим из критической секции добавления мешка
      sem_post(sem);  //разбудить пекаря
   } while(1);
}

PM   Вверх
OpenMan
Дата 7.7.2010, 08:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 19.4.2009

Репутация: нет
Всего: 1



Немного усложним вопрос.

Как я понимаю под виндой единственный способ использования семафоры - это всякие там 
hSemaphore = CreateSemaphore( 
NULL, // нет атрибута
cMax, // начальное состояние
cMax, // максимальное состояние
NULL // без имени
);

Задача немного меняется, теперь пекарь может забирать сразу до 10 мешков, если они есть.
Освобождать можно любое количество с помощью ReleaseSemaphore( Semaphore,N,0)
а вот опускать как это дело?

пекарь()
{
   do {
      sem_wait(sem); //спать, пока нет мешков
      pthread_mutex_lock(mutex); //входим в критическую секцию взятия мешка
      n =взять до 10 мешоков
      pthread_mutex_unlock(mutex); //выходим из критической секции взятия мешка
      печь булки
      for(int i=1; i<n; i++){
            sem_wait(sem);
     }
   } while(1);
}

Так что ли? Ведь в указанном вами способе 1 значение семафора равняется ровно 1 мешку.
PM MAIL   Вверх
xvr
Дата 7.7.2010, 09:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Цитата(OpenMan @  7.7.2010,  08:52 Найти цитируемый пост)
Так что ли?
Нет

Код

пекарь()
{
   do {
      sem_wait(sem); //спать, пока нет мешков
      int i=1;
      for(;i<10;++i)
       if (sem_trywait(sem)) break;
      pthread_mutex_lock(mutex); //входим в критическую секцию взятия мешка
      взять <i> мешков
      pthread_mutex_unlock(mutex); //выходим из критической секции взятия мешка
      печь булки
   } while(1);
}

PM MAIL   Вверх
Acer
Дата 7.7.2010, 10:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 652
Регистрация: 5.9.2007
Где: UA::DN

Репутация: нет
Всего: 8



bsa, а в данной задаче можно использовать ту же переменную isSleep, только если обьявить ее как volatile?
PM MAIL   Вверх
bsa
Дата 7.7.2010, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



volatile не значит atomic. В многопоточных программах можно использовать только атомарные операции с общими данными. Иначе race condition.
PM   Вверх
OpenMan
Дата 8.7.2010, 21:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 19.4.2009

Репутация: нет
Всего: 1



как я понимаю, аналог используемого sem_trywait(sem) в ВинАПИ - это WaitForSingleObject(sem, 0)
 с проверкой возвращенного значения на равненство 0.

Почему так
             for(;i<10;++i)
                 if (sem_trywait(sem)) break;
 не получится в моем случае, потому в моей задачи несколько различных "складов". Поэтому придется хранить количество мешков на каждом складе по отдельности и уменьшать\увеличивать при взятии.  Скажем так, первый склад - это обязательная очередь, у которой наивысший преоритет, а остальные - это ограниченные FIFO по размеру (например предел 3 мешка, если добавляется 4-й, то первый выкидывается) соответственно со средним преоритетом (если обязательная очередь пуста) и наименьшем. 

Просто как-то странно, что функциия ReleaseSemaphore может увеличивать счетчтк на любое количество, в то время как, уменьшать можно только на единицу.

А в обшем спасибо.
PM MAIL   Вверх
xvr
Дата 8.7.2010, 23:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Цитата(OpenMan @  8.7.2010,  21:25 Найти цитируемый пост)
Просто как-то странно, что функциия ReleaseSemaphore может увеличивать счетчтк на любое количество, в то время как, уменьшать можно только на единицу.
Это как раз закономерно - ReleaseSemaphore не может вызвать блокировку потока, который ее позвал, а WaitFor* может. И при этом непонятно как себя вести если счетчик семафора был не нулевой, но меньше, чем запрошенный декремент. Блокировать поток или не блокировать, сколько списывать со счетчика семафора, как сигнализировать вызывающему потоку и пр. Вызов WaitFor* в цикле позволяет программе самой решить, как разруливать такие ситуации


PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0548 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.