Здравствуйте, Lazin, Вы писали:
_>>Выделенное — лютый трындец. Сегодня a и b на один мьютекс попали, на следующем старте a и c попадут — это называется happy debugging. Имхо — изначальная идея порочная, все остальное только следствие из бажной концепции.
L>Это совершенно обычная вещь, так часто делают.
Согласен, так делают часто. К сожалению. И за это надо
L>Самый частый юзкейс — это когда у тебя много объектов и каждый нужно лочить отдельно,
Я буду продолжать утверждать, что такой юзкейс — всегда следствие грубой архитектурной ошибки или вообще отсутствия архитектуры как таковой.
Доказывается это элементарно. Основной выигрыш от многопоточного программирования состоит в возможности делать несколько задач параллельно. Неважно, что это за задачи — расчет чего-то тяжелого на 124 ядрах или просто 100500 потоков на одном ядре, которые спят на блокирующем I/O. Важно то, что каждый поток большую часть времени занят своим делом и не мешает остальным. Чтобы достичь такой нирваны, блокирующий или потенциально блокирующий обмен данными между потоками должен происходить как можно реже и в как можно меньшем количестве четко определенных локаций в коде.
"Много объектов, которые нужно лочить отдельно" рушат эту концепцию на корню и создают проблемы. Дедлоки в том числе.
S>Может как то явно определить для каждого lock layer глубину в иерархии и в lock-unlock складывать её в thread local стек с проверкой на 'больше' при push и на 'равно' при pop?
Это сильно упростит дело (решение о наличии/отсутствии дедлока принимается потоком локально, вместо того, чтобы сравнивать стеки вызовов разных потоков), да, но не хотелось бы, так как пострадает простота использования и модульность (нужно заранее знать порядок). Это пока запасной вариант, на случай если я ничего не придумаю
Здравствуйте, Lazin, Вы писали:
L>Я не планирую ничего исправлять, у меня в голове такой юзкейс: программист использует библиотеку для создания приложения, вдруг, в каких-нибудь условиях вылезает дедлок, программист делает специальный билд, в котором включен механизм обнаружения взаимных блокировок, запускает приложение, повторяет условия при которых возникала взаимная блокировка, библиотека видит условие возникновение делока, пишет простыню в stderr и вызывает terminate.
Из опыта:
(0.Дедлоки вполне себе ловятся при помощи debugdiag от мелкософта. Да и просто достаточно снять ручной дамп и заблокированные треды как на ладони.)
1.специальный билд слегка меняет тайминги и дедлок не повторяется. Вообще никак. Хоть ты на голову встань.
2.приходит багрепорт из продакшена о стабильном зависоне. Специальный билд у клиента поставить невозможно. Снимаете ручной дамп, видите заблокированный тред и всей конторой не можете понять, как потоки вообще умудрились оказаться в таком состоянии, т.к. этого "не может быть никогда". Это следствие архитектурных просчетов.
3. Находите один лок, чините, создаете три новых. Следствие архитектурных просчетов.
L>>Во время тестирования абсолютно невозможно отловить все возможные варианты, особенно, когда мьютексы создаются на каждый чих. Если детектор выявил Х дедлоков во время тестирования, это вовсе не гарантирует, что нашли их все. Более того, это говорит о том, что код плохо спроектирован с точки зрения межпотокового взаимодействия и скорее всего, в нем притаились другие дедлоки, которые просто ждут своего шанса.
L>В том то и дело, что я свожу сложную проблему — детектор дедлоков для N мьютексов — к простой, детектор дедлоков для LockLayer-ов, которых в типичной программе должно быть один или два
Извини, но это лечение туберкулеза перцовым пластырем.
Здравствуйте, Lazin, Вы писали:
S>>Может как то явно определить для каждого lock layer глубину в иерархии и в lock-unlock складывать её в thread local стек с проверкой на 'больше' при push и на 'равно' при pop?
L>Это сильно упростит дело (решение о наличии/отсутствии дедлока принимается потоком локально, вместо того, чтобы сравнивать стеки вызовов разных потоков), да, но не хотелось бы, так как пострадает простота использования и модульность (нужно заранее знать порядок). Это пока запасной вариант, на случай если я ничего не придумаю
Ещё надо учитывать, что связка между lock-layer может быть сильной-слабой, т.е. блокировка какого либо уровня может требовать блокировки более высокого уровня или не требовать. Они синхронизируют какие то модификации разных уровней и иногда эти модификации могут быть выполнены независимо друг от друга, а иногда нет. В первом случае проверка нестрогая, просто больше, во втором случае обязательно наличие блокировки предыдущего уровня.
В принципе можно на организационном уровне как то решать, просто для порядка требовать в конструкторе guard'а ссылку на guard более высокого уровня. Для общности можно ввести null-lock-level, от которого плясать во всех точках входа потока.
L>>Это совершенно обычная вещь, так часто делают. L>Согласен, так делают часто. К сожалению. И за это надо
Альтернативы то какие? Убить параллелизм используя один мьютекс?
L>>Самый частый юзкейс — это когда у тебя много объектов и каждый нужно лочить отдельно, L>Я буду продолжать утверждать, что такой юзкейс — всегда следствие грубой архитектурной ошибки или вообще отсутствия архитектуры как таковой.
I dunnu WTF R U talking about
L>Доказывается это элементарно. Основной выигрыш от многопоточного программирования состоит в возможности делать несколько задач параллельно. Неважно, что это за задачи — расчет чего-то тяжелого на 124 ядрах или просто 100500 потоков на одном ядре, которые спят на блокирующем I/O. Важно то, что каждый поток большую часть времени занят своим делом и не мешает остальным. Чтобы достичь такой нирваны, блокирующий или потенциально блокирующий обмен данными между потоками должен происходить как можно реже и в как можно меньшем количестве четко определенных локаций в коде.
Ну так в коде это может быть одно место но лочиться в этом месте могут разные объекты, в зависимости от данных. Я не спорю с тем, что потоки не должны слишком часто взаимодействовать друг с другом (сам писал об этом), но это не имеет никакого отношения к корректности многопоточных программ.
L>"Много объектов, которые нужно лочить отдельно" рушат эту концепцию на корню и создают проблемы. Дедлоки в том числе.
Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами, да, она сложнее чем coarse grained синхронизация (global lock самый простой вариант из всех возможных, тщательно организованная fine grained синхронизация, это очень сложно). Но только вот не надо говорить что всем подходит первое, а второе — ошибка архитектуры. Может я биллинг сервис пишу какой-нибудь или RTB систему, которая баннры должна показывать и параллельно с этим — аукционы в баннерной сети выигрывать. Есть ТЗ по которому может потребоваться сделать более сложную систему в которой возможно возникновение дедлоков и прочих ништяков. В общем, твой посыл понятен, но вот спорить на эту тему, на мой взгляд — бессмысленно.
L>(0.Дедлоки вполне себе ловятся при помощи debugdiag от мелкософта. Да и просто достаточно снять ручной дамп и заблокированные треды как на ладони.)
у меня все на портируемом С++ написано и может работать там где нет debugdiag от майкрософта
L>1.специальный билд слегка меняет тайминги и дедлок не повторяется. Вообще никак. Хоть ты на голову встань.
я ищу дедлоки между LockLayer-ами, а не мьютексами, а значит отловлю не только реальные дедлоки на мьютексах но даже потенциально возможные
L>2.приходит багрепорт из продакшена о стабильном зависоне. Специальный билд у клиента поставить невозможно. Снимаете ручной дамп, видите заблокированный тред и всей конторой не можете понять, как потоки вообще умудрились оказаться в таком состоянии, т.к. этого "не может быть никогда". Это следствие архитектурных просчетов. L>3. Находите один лок, чините, создаете три новых. Следствие архитектурных просчетов.
и эти люди учат нас писать многопоточный код
L>Извини, но это лечение туберкулеза перцовым пластырем.
Не все на свете сводится к простым схемам взаимодействия между потоками и "неправильная" арх. (с твоей точки зрения), может быть правильным решением той или иной задачи.
S>Ещё надо учитывать, что связка между lock-layer может быть сильной-слабой, т.е. блокировка какого либо уровня может требовать блокировки более высокого уровня или не требовать. Они синхронизируют какие то модификации разных уровней и иногда эти модификации могут быть выполнены независимо друг от друга, а иногда нет. В первом случае проверка нестрогая, просто больше, во втором случае обязательно наличие блокировки предыдущего уровня.
S>В принципе можно на организационном уровне как то решать, просто для порядка требовать в конструкторе guard'а ссылку на guard более высокого уровня. Для общности можно ввести null-lock-level, от которого плясать во всех точках входа потока.
Это все хорошо, можно действительно позволять задавать родительский lock layer, это упростит анализ. Я наверное так сделаю, но только это будет опциональная возможность. Ну и для начала нужно сделать для самого сложного случая — когда явной связки между lock layer-ами нет. Технически, моей схеме синхронизации это не нужно, нужно просто проверить на отсутсвие рекурсии (самая базовая проверка, маст хэв), потом можно проверить на отсутствие циклов (желательно, так как ожидания пользователя скорее всего будут включать и эту проверку) ну и после этого можно позволить задать пользователю дополнительные ограничения, то о чем ты говоришь, и проверять еще и их. Не вижу особых проблем для этого.
Пока что меня больше интересует то, как это сделать технически. Я думал делать так: есть некий глобальный объект — DeadlockDetector, у него есть метод add и метод remove. В методе lock lock layer-а есть thread local переменная, при вызове lock, поток смотрит туда, если она не инициализрована — значит он lock layer нулевого уровня, мы можем инициализировать ее и передать в DeadlockDetector::add ссылку на себя (объект LockLayer), в котором она добавится в конец списка (один atomic exchange). Если переменная в thread local уже инициализирована, значит мы — не первый lock layer и ниже по стеку уже есть еще один лок. Мы можем пройтись по стеку вниз и получить ссылки на низлежащие lock-layer-ы. Проблема пока только в том, что нужно сравнивать стеки lock layer-ов, я не придумал как это делать пока, так же не ясно, как чистить память (может можно и не чистить ее совсем).
Здравствуйте, Lazin, Вы писали:
L>>Согласен, так делают часто. К сожалению. И за это надо
L>Альтернативы то какие? Убить параллелизм используя один мьютекс?
Наоборот. Довести его до максимума, запрещая блокировать что попало на каждый чих и заставляя обдумывать каждую блокировку.
L>>>Самый частый юзкейс — это когда у тебя много объектов и каждый нужно лочить отдельно, L>>Я буду продолжать утверждать, что такой юзкейс — всегда следствие грубой архитектурной ошибки или вообще отсутствия архитектуры как таковой.
L>I dunnu WTF R U talking about
Какое слово непонятно?
L>Ну так в коде это может быть одно место но лочиться в этом месте могут разные объекты, в зависимости от данных.
L>>"Много объектов, которые нужно лочить отдельно" рушат эту концепцию на корню и создают проблемы. Дедлоки в том числе.
L>Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами,
Во-первых, если разные потоки работают с разными объектами, не пересекаясь, зачем вообще синхронизация? Если же им требуется делить один и тот же объект и, соответственно, блокировать друг друга, то возникает вопрос — а есть ли, собственно, выгода от многопоточности? Не получится ли так, что потоки 100 мс работают, а потом еще 200 мс ждут в очереди доступа к залоченному ресурсу?
L>да, она сложнее чем coarse grained синхронизация (global lock самый простой вариант из всех возможных, тщательно организованная fine grained синхронизация, это очень сложно). Но только вот не надо говорить что всем подходит первое, а второе — ошибка архитектуры.
Как правило, пытаясь достичь второго, умудряются лишь создать кошмарную архитектуру
L>Может я биллинг сервис пишу какой-нибудь или RTB систему, которая баннры должна показывать и параллельно с этим — аукционы в баннерной сети выигрывать.
Не имеет значения. Никакого.
L>Есть ТЗ по которому может потребоваться сделать более сложную систему в которой возможно возникновение дедлоков и прочих ништяков.
Возникновение дедлока — это всегда ошибка архитектуры. Не может требоваться система, в которой могут возникать дедлоки. Такая система бесполезна.
Вот тебе пример — построили однопутную железную дорогу и разъезд на ней, чтобы можно было пропускать встречные поезда. В первый же день оказалось, что обычные до этих мест длинные товарняки на разъезд полностью не влезают. В результате — дедлок и миллионы убытка в день и стоящая раком сеть. Кто виноват — машинист поезда или все же горе-проектировщики разъезда? Можно, конечно, заняться fine grained синхронизацией, разрезать поезда, заказать за миллиарды дополнительные локомотивы, нагрузить другие узловые станции передержкой вагонов, а перегоны увеличенным количеством поездов и справиться с ситуацией, получив задранные до небес накладные расходы, а можно выгнать горе-проектировщиков и увеличить разъезд так, чтобы дедлока не возникало.
L>В общем, твой посыл понятен, но вот спорить на эту тему, на мой взгляд — бессмысленно.
Видишь ли в чем дело. Ты аргументируешь сложностью системы. А я считаю сложность системы плохим, негодным оправданием для архитектурных просчетов. Я всегда свожу сложную систему, какой бы сложной она не казалась, к простой системе простых систем. Это общепринятый инженерный подход. И не вижу ни одной причины так не делать.
Здравствуйте, Lazin, Вы писали:
L>у меня все на портируемом С++ написано и может работать там где нет debugdiag от майкрософта
там будет gdb. Там, где gdb нет, с многопоточностью тоже будут недетские напряги.
L>>1.специальный билд слегка меняет тайминги и дедлок не повторяется. Вообще никак. Хоть ты на голову встань. L>я ищу дедлоки между LockLayer-ами, а не мьютексами, а значит отловлю не только реальные дедлоки на мьютексах но даже потенциально возможные
Не отловишь.
Ты пытаешься искать лок в динамике. А динамика — она такая. Динамичная с потецниально неограниченным количеством состояний.
L>>2.приходит багрепорт из продакшена о стабильном зависоне. Специальный билд у клиента поставить невозможно. Снимаете ручной дамп, видите заблокированный тред и всей конторой не можете понять, как потоки вообще умудрились оказаться в таком состоянии, т.к. этого "не может быть никогда". Это следствие архитектурных просчетов. L>>3. Находите один лок, чините, создаете три новых. Следствие архитектурных просчетов.
L>и эти люди учат нас писать многопоточный код
Именно. "Эти люди" на своем опыте знают, чем именно заканчивается попытка лечить архитектурные просчеты изолентой.
L>>Извини, но это лечение туберкулеза перцовым пластырем.
L>Не все на свете сводится к простым схемам взаимодействия между потоками и "неправильная" арх. (с твоей точки зрения), может быть правильным решением той или иной задачи.
Архитектура, принципиально допускающая образование дедлоков, не может быть правильным решением какой бы то ни было задачи. Она может быть условно приемлимым в определенных рамках решением, но это не относится к разработке софта.
Здравствуйте, Lazin, Вы писали:
L>Пока что меня больше интересует то, как это сделать технически. Я думал делать так: есть некий глобальный объект — DeadlockDetector, у него есть метод add и метод remove. В методе lock lock layer-а есть thread local переменная, при вызове lock, поток смотрит туда, если она не инициализрована — значит он lock layer нулевого уровня, мы можем инициализировать ее и передать в DeadlockDetector::add ссылку на себя (объект LockLayer), в котором она добавится в конец списка (один atomic exchange). Если переменная в thread local уже инициализирована, значит мы — не первый lock layer и ниже по стеку уже есть еще один лок. Мы можем пройтись по стеку вниз и получить ссылки на низлежащие lock-layer-ы. Проблема пока только в том, что нужно сравнивать стеки lock layer-ов, я не придумал как это делать пока, так же не ясно, как чистить память (может можно и не чистить ее совсем).
Ну проверка на deadlock бесплатной всё равно не будет, thread-local стек блокировок можно и внутри DeadlockDetector сделать, что то вроде этого:
void DeadlockDetector::on_lock(LockLayerImpl& layer, size_t hash) {
lock_stack& thread_locks = get_thread_locks();
// проверяем отсутствие пары (layer, hash) в стеке
// пары (layer, H) могут присутствовать сверху в порядке hash
// т.е. если сверху тот же layer, достаточно проверить hash order
check_lock_loop(thread_locks, layer, hash);
thread_locks.push_back(std::make_pair(&layer, hash));
}
void DeadlockDetector::on_unlock(LockLayerImpl& layer, size_t hash) {
lock_stack& thread_locks = get_thread_locks();
// проверяем соответствие пары (layer, hash) последнему элементу
check_last_lock(thread_locks, layer, hash);
thread_locks.pop_back();
}
Память я думаю можно и не чистить, всё равно DeadlockDetector глобальный. Атомики не нужны, thread_locks всё равно thread-local.
L>Наоборот. Довести его до максимума, запрещая блокировать что попало на каждый чих и заставляя обдумывать каждую блокировку.
А подробнее вот об этом можно? У меня есть 1000 объектов, вероятность того, что 2 потока будут использовать в один момент вермени один объект — один из тысячи. Т.е. локов много, вероятность блокировки — очень низка, все потоки большую часть времени работают не мешая друг другу. Думаю это всем понятный кейс. Ты же предлагаешь нечто загадочное.
L>>Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами,
L>Во-первых, если разные потоки работают с разными объектами, не пересекаясь, зачем вообще синхронизация? Если же им требуется делить один и тот же объект и, соответственно, блокировать друг друга, то возникает вопрос — а есть ли, собственно, выгода от многопоточности? Не получится ли так, что потоки 100 мс работают, а потом еще 200 мс ждут в очереди доступа к залоченному ресурсу?
Смотри пример выше.
L>Как правило, пытаясь достичь второго, умудряются лишь создать кошмарную архитектуру
Общие фразы опять.
L>Возникновение дедлока — это всегда ошибка архитектуры. Не может требоваться система, в которой могут возникать дедлоки. Такая система бесполезна.
Никто и не утверждал что это нормально, я имел ввиду простую вещь — делаем сложную систему с нетривиальной синхронизацией — возможные дедлоки в следствии ошибок. Ты и сам это повторяешь постоянно. Я не говорил что нужно делать системы в которых дедлоки возможны в принципе.
L>Видишь ли в чем дело. Ты аргументируешь сложностью системы. А я считаю сложность системы плохим, негодным оправданием для архитектурных просчетов. Я всегда свожу сложную систему, какой бы сложной она не казалась, к простой системе простых систем. Это общепринятый инженерный подход. И не вижу ни одной причины так не делать.
Ты пока только общие фразы говришь и все. Вот мой пример выше, как это можно сделать просто и без блокировок (или с небольшим количеством блокировок)? Я не вижу смысла продолжать говорить "в общем", нужно либо конкретные примеры рассматривать (что сложно) либо забить на это, ибо дальше мы точно не продвинемся
Здравствуйте, sokel, Вы писали:
S>Ну проверка на deadlock бесплатной всё равно не будет, thread-local стек блокировок можно и внутри DeadlockDetector сделать, что то вроде этого:
Ааа, sorry, ступил. Это же только на цикл проверка, для проверки порядка действительно без синхронизации не обойтись.
Здравствуйте, sokel, Вы писали:
S>Ааа, sorry, ступил. Это же только на цикл проверка, для проверки порядка действительно без синхронизации не обойтись.
В лоб что то такое только:
// check_lock_order:
lock_layer_transitions;
foreach(prev_layer in thread_locks) {
assert(!layer.transitions.contains(prev_layer));
prev_layer.transitions.insert(layer);
}
Здравствуйте, sokel, Вы писали:
S>Но это дорого будет наверное...
Можно пронумеровать все слои в конструкторе и хранить transition-map в DeadlockDetector в виде двумерного массива. Тогда можно будет заполнять случающиеся переходы без блокировки. Только вот проверка будет не синхронизирована, но её можно и постфактум выполнить.
BFE>>Нужно всего лишь гарантировать, что ни при каких условиях не возникает ситуации, когда в одном стеке вызовов присутствует более одной блокировки.
L>Совершенно верно. Правильный способ это сделать — локализовать и минимизировать межпотоковое взаимодействие. Разрешать всем потокам общаться друг с другом напрямую, во-первых, бессмысленно, во-второых, ведет к серьезным ошибкам.
К каким ещё серьезным ошибкам? Если никакая из ниток не пытается одновременно залочить более одного мьютекса, то никакой deadlock невозможен. Тогда зачем, в общем случае, запрещать потокам обращаться друг с другом напрямую?
Здравствуйте, landerhigh, Вы писали:
L>Архитектура, принципиально допускающая образование дедлоков, не может быть правильным решением какой бы то ни было задачи.
Я так понимаю, что правильно написанная deadlock-prevention библиотека гарантирует отсутствие дедлоков. Я не говорю про библиотек Lazin'а, а про общий случай.
Здравствуйте, B0FEE664, Вы писали:
L>>Архитектура, принципиально допускающая образование дедлоков, не может быть правильным решением какой бы то ни было задачи. BFE>Я так понимаю, что правильно написанная deadlock-prevention библиотека гарантирует отсутствие дедлоков.
Нет. Она не может такого гарантировать. Она не может предотвращать дедлоки. Они может их обнаруживать.
В самом лучшем случае, допустим, что мы такую библиотеку написали. Система в продакшене. Допустим, эта библиотека обнаружила дедлок.
Что она должна сделать?
Здравствуйте, B0FEE664, Вы писали:
L>>Совершенно верно. Правильный способ это сделать — локализовать и минимизировать межпотоковое взаимодействие. Разрешать всем потокам общаться друг с другом напрямую, во-первых, бессмысленно, во-второых, ведет к серьезным ошибкам.
BFE>К каким ещё серьезным ошибкам? BFE>Если никакая из ниток не пытается одновременно залочить более одного мьютекса, то никакой deadlock невозможен.
Если любая нитка может общаться с любой другой ниткой, то дедлок весьма возможен даже в этом случае. Нарисуй картинку
BFE>Тогда зачем, в общем случае, запрещать потокам обращаться друг с другом напрямую?
Потому что в общем случае им не о чем друг с другом говорить. Поток нужен, чтобы выполнять работу. Он должен получать задания от управляющего потока и сообщать ему о результатах работы. Все. Болтовня кого попало с кем попало никому не нужна.
Есть интересные случаи параллельных алгоритмов, но они тоже сводятся к этому паттерну.
Здравствуйте, sokel, Вы писали:
S>Здравствуйте, sokel, Вы писали:
S>>Но это дорого будет наверное...
S>Можно пронумеровать все слои в конструкторе и хранить transition-map в DeadlockDetector в виде двумерного массива. Тогда можно будет заполнять случающиеся переходы без блокировки. Только вот проверка будет не синхронизирована, но её можно и постфактум выполнить.
Хотя можно и на месте можно lock-free получать факт deadlock'а потенциального с двумерным массивом. Предположим два потока одновременно регистрируют взаимно невозможные переходы layer1-layer2 и layer2-layer1. Нужно упорядочить регистрацию переходов. Для этого просто будем использовать половину матрицы, т.е. оба перехода фиксируем в переменной transitions[1][2] (упорядочиваем индексы). Прямой переход — значение 1, обратный переход — значение 2. Ну и дальше atomic CAS, допустимы изменения 0-1, 1-1, 0-2, 2-2. При фиксации попыток изменений 1-2 или 2-1 сигнализируем deadlock.
Здравствуйте, Lazin, Вы писали:
L>А подробнее вот об этом можно? У меня есть 1000 объектов, вероятность того, что 2 потока будут использовать в один момент вермени один объект — один из тысячи.
Во-первых, что это за 1000 объектов? Почему они доступны сразу всем потокам? Почему нельзя их разделить и передать потокам в эксклюзивоное пользование? Нельзя ли поручить общение с этим объектами одному потоку? И так далее...
L>Т.е. локов много, вероятность блокировки — очень низка, все потоки большую часть времени работают не мешая друг другу.
ВременнАя вероятность никого не интересует. Вероятность блокировки в этом случае строго равна единице, она рано или еще раньше произойдет. И будет ли это просто конкурентный лок или же он перерастет в полноценный дед лок, зависит исключительно от архитектуры. Если граф вызовов объекта под локом ни в каком случае не заходит в другой лок, мы спасены. Но гарантировать это можно исключительно правильной архитектурой, в которой эта гарантия обеспечивается дизайном.
L>Думаю это всем понятный кейс. Ты же предлагаешь нечто загадочное.
Я предлагаю простейшее решение. Чего тут моежет быть непонятного?
L>>>Это опять же fine grained синхронизация, которая часто бывает нужна, чтобы разные потоки могли работать параллельно с разными объектами,
L>>Во-первых, если разные потоки работают с разными объектами, не пересекаясь, зачем вообще синхронизация? Если же им требуется делить один и тот же объект и, соответственно, блокировать друг друга, то возникает вопрос — а есть ли, собственно, выгода от многопоточности? Не получится ли так, что потоки 100 мс работают, а потом еще 200 мс ждут в очереди доступа к залоченному ресурсу?
L>Смотри пример выше.
Ответь на вопрос "зачем". Любое решение должно быть обосновано.
L>>Как правило, пытаясь достичь второго, умудряются лишь создать кошмарную архитектуру
L>Общие фразы опять.
Основаны на конкретных событиях.
L>>Возникновение дедлока — это всегда ошибка архитектуры. Не может требоваться система, в которой могут возникать дедлоки. Такая система бесполезна.
L>Никто и не утверждал что это нормально, я имел ввиду простую вещь — делаем сложную систему с нетривиальной синхронизацией — возможные дедлоки в следствии ошибок.
.. в проектировании. Не можете сделать сложную систему с нетривиальной синхронизацией без дедлоков, лучше сделайте систему простой, а синхронизацию тривиальной, но без дедлоков.
Исправить архитектурные ошибки библиотекой обнаружения локов не выйдет.
L>Ты и сам это повторяешь постоянно. Я не говорил что нужно делать системы в которых дедлоки возможны в принципе.
L>>Видишь ли в чем дело. Ты аргументируешь сложностью системы. А я считаю сложность системы плохим, негодным оправданием для архитектурных просчетов. Я всегда свожу сложную систему, какой бы сложной она не казалась, к простой системе простых систем. Это общепринятый инженерный подход. И не вижу ни одной причины так не делать.
L>Ты пока только общие фразы говришь и все. L>Вот мой пример выше, как это можно сделать просто и без блокировок (или с небольшим количеством блокировок)? Я не вижу смысла продолжать говорить "в общем", нужно либо конкретные примеры рассматривать (что сложно) либо забить на это, ибо дальше мы точно не продвинемся
Ты сначала защити свое изначальное решение иметь 1000 объектов, расшаренных на все потоки. Больше информации, короче!
Ошибки в фундаменте здания несколько поздно чинить при установке лифтов, может оказаться, что и ни 1000 объектов не надо, ни сотен потоков.