LINUX.ORG.RU

pthread_mutex дождаться освобождения без захвата мьютекса


0

1

Доброго времени суток!

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

Другими словами мне нужно разрешить следующую ситуацию:

Поток 1 - поиск объекта и захват мьютекса (успешный) - trylock

Поток 2 - поиск объекта и попытка захвата (не успешная) - trylock, ожидаем освобождения мьютекса - lock.

Поток 1 - удаление объекта из списка, освобождение мьютекса, уничтожение объекта и мьютекса

Поток 2 - продолжение работы, освобождение УНИЧТОЖЕННОГО мьютекса и повторный рекурсивный поиск объекта (он не будет найден - это нормальная ситуация)

Таким образом, происходит уничтожение в первом потоке мьютекса, блокиремого вторым, что очевидно ненормально. В качестве выхода вижу вариант ожидания освобождения мьютекса без его захвата, но не знаю, есть ли такая возможность (C++, Linux, POSIX). В качестве некрасивого варианта вижу цикл с trylock и sleep.

Спасибо!

Ответ на: комментарий от arsi

нет, все таки wrlock нужен, два потока могут одновремено пытаться добавить элемент в конец списка, тогда один из добавляемых объектов может быть утерян

kirichenkoga
() автор топика
Ответ на: комментарий от Manhunt

Не пройдет вот эта часть:

 
  if(!was_in_processing) 
  { 
    process_packets(id, node);
  } 

Если объект в процессе, то пакет не будет обработан, а он должен быть обработан. В этом вся загвоздка. Алгоритм можно сделать так:

 
  if(!was_in_processing) 
  { 
    process_packets(id, node); 
  } 
  else 
  { 
    ...; //ждем чего-нибудь 
    handle_packet(packet); //или goto на начало процедуры, или тело процедуры обернуть в цикл, повторяющийся до тех пор, пока пакет не будет обработан 
  } 

Вот я не могу понять, как выполнить действие «ждем чего-нибудь», так, чтобы не делать overhead и не задерживать обработку

kirichenkoga
() автор топика
Ответ на: комментарий от kirichenkoga

> Если объект в процессе, то пакет не будет обработан, а он должен быть обработан.

Несколькими строчками выше пакет был помещен в очередь к этому объекту. Если (was_in_processing==true), то пакет из очереди выгребет та нить, которая урвала себе (was_in_processing==false) и которая сейчас находится в функции process_packets() или вот-вот войдет в эту функцию.

...; //ждем чего-нибудь


Ожидание заключается в том, что пакет положили в очередь. Всё, данной нити этот пакет уже не интересен - он передан в другую нить. В результате обходимся без трайлоков и прочей запутанной синхронизации.

Manhunt ★★★★★
()
Ответ на: комментарий от tailgunner

> Ага. Какие-то контексты, узлы...

Название «контекст» я зря приплел, поскольку ТС использовал для (почти) этой же сущности слово «объект».

А «узлы» ... нужно было как-то обозвать ту структуру, вот и обозвал первым попавшимся словом.

ты на Яве не пишешь?


На С++ пишу. Нередко приходится писать высокоуровневую бизнес-логику, так что почти жабка, да.

Предложишь более удачный код?

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

> Предложишь более удачный код?

Нет, конечно. Чтобы привести более удачный, нужно писать программу, транслировать, а еще лучше - и отлаживать. Я не настолько сильно хочу помочь топикстартеру. Но могу точно сказать, что либо Context, либо Node у меня не было бы.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Но могу точно сказать, что либо Context, либо Node у меня не было бы.

У них роли разные совсем. Node посвящен взаимодействию между нитями (поля «packets» и «is_in_processing» ни для чего больше не нужны), а Context посвящен логике обработки очередного пакета. Поэтому есть смысл держать 2 разные сущности, вопрос лишь в их названиях..

Чтобы привести более удачный, нужно писать программу, транслировать, а еще лучше - и отлаживать.


Можно просто привести код, иллюстрирующий идею. Без компиляции и отладки.

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

Можно просто привести код, иллюстрирующий идею. Без компиляции и отладки.

Иллюстрация идеи - это псевдокод. По-моему, моя идея настолько проста, что даже псевдокода не требует, но вот что-то вроде Питона:

def get_processable_packet():
  mutex.lock()
  available_packets = [p for p in packets if not p.processing]
  ret = available_packets[0]
  ret.processing = True
  mutex.unlock()
  return p

def put_processed_packet(p):
  assert p.processing
  mutex.lock()
  p.processing = False
  mutex.unlock()

def drop_processed_packet(p):
  assert p.processing
  mutex.lock()
  packets.pop(p)
  mutex.unlock()


Нити пула обработчиков вызывают эти три функции.

Сигнализация о появлении пакета в списке packets не приведена - тут надо писать именно Си-вызовы операций с мютексом и условной переменной, не для псевдокода работа.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Не очень понимаю. В постановке задачи ( http://www.linux.org.ru/jump-message.jsp?msgid=6487386&cid=6487839 ) есть связанные через ID «пакеты» и «объекты». Иногда после обработки пакета объект остается жить, а иногда - должен быть удален вместе со всеми упоминаниями об этом ID. И главная сложность возникает тогда, когда разные «пакеты» устраивают толкотню за право поработать с одним и тем же «объектом».

А в твоем описании есть только пакеты, объекты вообще не упоминаются. Как это должно работать?

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

> В постановке задачи ( http://www.linux.org.ru/jump-message.jsp?msgid=6487386&cid=6487839 ) есть связанные через ID «пакеты» и «объекты».

Я не берусь решать задачу, которой не понимаю, и осваивать EAP тоже не собираюсь. Вот это: http://www.linux.org.ru/jump-message.jsp?msgid=6487386&cid=6487504 я понимаю (или думаю, что понимаю), для него и написал псевдокод. Разницу между «пакетом» и «объектом» не ухватываю, но, думаю, для алгоритма блокировки она неважна. Если нужно хранить целый список пакетов («объект»?), это делается ровно так же.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> понимаю (или думаю, что понимаю)

Мде. Понимание задачи у нас с тобой точно разное.

Manhunt ★★★★★
()
Ответ на: комментарий от tailgunner

В моей задаче, пакет - это набор инструкций, приходит по сети. Потоком этих пакетов я не могу управлять, я только должен «исполнить» эти инструкции. Инструкции выполняются над некоторым переменным списком объектов. В результате применения инструкций к объекту, этот объект изменяет свое состояние (сюда я отношу и уничтожение объекта).

Пример, где моя задача будет использована: поступили подряд 2 пакета инструкций к одному объекту. Начал выполняться «условно первый» пакет, обработка второго пакета должна ждать. Как только первый пакет обработан, второй поток проснувшись (объект освобождается) должен проверить существует ли еще объект (ведь он мог быть удален после получения пакета инструкций вторым потоком, но до освобождения объекта), если еще существует - то применить к нему инструкции, в противном случае, создать новый объект и применить инструкции к нему.

Я не могу выбирать пакет для обработки - он спускается свыше ,фактически потоки вызывают написанную мной функцию, что-то вроде process(пакет инструкций). Вот в этом самом process я должен дождаться освобождения целевого объекта, проверить его существование (после освобождения!), пересоздать при необходимости и выполнить пакет инструкций.

В приведенном примере происходит выборка пакета инструкций из пула, чего у меня нет. И не учитывается что 2 пакета для одного объекта - блокируются, а для разных работают паралельно.

kirichenkoga
() автор топика
Ответ на: комментарий от tailgunner

Я согласен, что это распространено, но для меня это не подходит. Если делать как в приведенном примере, то надо переделывать все приложение, на что у меня нет полномочий.

Попробую упростить задачу. Есть некоторая структура данных, в которой есть признак занятости структуры (булева переменная, мьютекс или еще что-то). Пусть эта структура занята некоторым потоком, который эту структуру может уничтожить (включая и хранящийся в ней признак - память освобождается и может быть перезаписана чем-нибудь в последствии). Необходимо во втором потоке дождаться освобождения (разблокирования) этой структуры не прибегая к внешним по отношению к ней объектам. Вариант с циклической проверкой булевой переменной не подходит - нет гарантии, что между итерациями проверки не произойдет уничтожение объекта (то есть, память им занимаемая может быть перезаписана - последствия печальные). Аналогично с мьютексом при использовании стандартного lock - мьютекс занимается первым потоком, а второй его уничтожает. Замечу, что ожидать освобождения объекта могут несколько потоков, поэтому если хранить признак отдельно - то необходимо дополнительно определять факт возможности уничтожения этого признака. Идеальным выходом было бы ожидание мьютекса без его последующей блокировки, но такой возможности как я понимаю не предусмотрено.

Использование внешних по отношению в объекту признаков блокировки приведет к блокированию потоков, работающих с разными объектами - а это не хорошо.

Наверно на этом этапе остановлюсь на выдаче ошибки при попытке захвата заблокированного объекта - все таки это несколько нештатная (хоть и вероятная) ситуация.

Также интересно получается, что при добавлении объекта после блокирования всего списка на запись необходимо повторить поиск объекта, чтобы избежать добавления объектов с одинаковым идентификатором от двух потоков одновременно.

kirichenkoga
() автор топика
Ответ на: комментарий от tailgunner

А не подскажешь, какое примерное количество мьютексов ОС перенесет без напрягов, 1000-2000 это близко к пределу (Linux, 64 бит, на нем запущен только разрабатываемый сервис)?

Просто для меня дико, почему изменение состояния ОДНОГО ОТДЕЛЬНОГО объекта должно блокировать изменение состояния ДРУГИХ объектов (я имею в виду признак занятости).

Спасибо!

kirichenkoga
() автор топика
Ответ на: комментарий от kirichenkoga

> А не подскажешь, какое примерное количество мьютексов ОС перенесет без напрягов, 1000-2000 это близко к пределу

Думаю, это очень далеко от предела.

Просто для меня дико, почему изменение состояния ОДНОГО ОТДЕЛЬНОГО объекта должно блокировать изменение состояния ДРУГИХ объектов

Не вижу у себя ничего похожего.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Я имел в виду другой пост, по поводу того, что на каждый объект делают только идиоты. В моем случае не будет единовременно существовать больше 1-2 тысяч объектов, поэтому по мьютексу на объект я считаю допустимым (опять таки, в моей задаче)

kirichenkoga
() автор топика
Ответ на: комментарий от kirichenkoga

> В моем случае не будет единовременно существовать больше 1-2 тысяч объектов, поэтому по мьютексу на объект я считаю допустимым

Если ты можешь гарантировать, что объектов будет не больше 1-2 тысяч, то конечно. Но, насколько я понимаю, это вообще не от тебя зависит.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Да, в общем случае зависит не от меня, но таков предел оборудования (клиент разрабатываемого сервера), врятли в ближайщие несколько лет следует ждать скачка пропускной способности на порядок и больше

kirichenkoga
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.