|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Прочитал у Альбахари , что это обычно приватный объект: авторThe synchronizing object is typically private (because this helps to encapsulate the locking logic) Т. е. он разбирает какой-то конкретный случай, когда логика работы с многопоточными данными вся в одном классе инкапсулирована. А что, если мне эти данные надо давать использовать многим другим классам - т. е. сделать их публичными? Например, у меня схема такая: класс, который что-то делает (считает, например) - расчётный класс Calc; класс, который хранит результаты и выводит их на экран: Код: c# 1. 2. 3. 4.
Свойство ResultValues должно иметь многопоточный доступ - Calc постоянно пишет в это свойство, а Result постоянно читает из него и показывает результаты. Проблема в том, что ResultValues - постоянно обновляет свои данные по мере расчёта. При этом по мере расчёта надо показывать промежуточные результаты. Т. е. нужно как-то синхронизировать ResultValues между классами Calc и Result. А поскольку ResultValues это публичное свойство, то мне приходится придумывать некий синхронизирующий объект, который надо шарить между этими двумя классами. Я делаю это так - создаю класс приложения, в который помещаю объекты Calc, Results и синхронизирующий объект. Таким образом я могу расшарить один и тот же синхронизирующий объект между Calc и Result - передавать его через конструкторы этих классов или прямо в те их методы, который используют свойство ResultValues. Т. е. моя схема отличается от того, что говорит Альбахари, что логика синхронизации должна быть инкапсулирована в одном классе. Я делаю всё правильно, или можно придумать схему лучше для моего случая? ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 07:19 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
А что, если сделать само свойство ResultValues синхронизируемым объектом, и везде, где используется это свойство - неважно, в своём классе, в котором объявлено это свойство, или в чужих - писать lock(ResultValues)? Был ещё вариант, сделать это свойство приватным, но добавить публинчые обёртки над этим свойством, типа GetResultValues, AddNewValuesToResult и т. п., которые бы под локом делали бы свою работу. Но показалось, что это слишком мудрёный вариант, который мало что даёт. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 07:22 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987А что, если сделать само свойство ResultValues синхронизируемым объектом, и везде, где используется это свойство - неважно, в своём классе, в котором объявлено это свойство, или в чужих - писать lock(ResultValues)? Был ещё вариант, сделать это свойство приватным, но добавить публинчые обёртки над этим свойством, типа GetResultValues, AddNewValuesToResult и т. п., которые бы под локом делали бы свою работу. Но показалось, что это слишком мудрёный вариант, который мало что даёт. Первый вариант плох тем, что невозможно заставить или как-то сообщить пользователю класса Result, что с ResultValues надо работать именно так. А второй хорош тем, что я заставляю пользователя ResultValues работать с ним только через методы, которые уже внутри используют блокировку. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 07:24 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Возьми ConcurrentBag это тот же лист, только потокобезопасный. Хотя тут очередь больше подойдет ConcurrentQueue Все потокобезопасные классы ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 07:27 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Dima TВозьми ConcurrentBag это тот же лист, только потокобезопасный. Хотя тут очередь больше подойдет ConcurrentQueue Все потокобезопасные классы Мне кажется, что потокобезопасные классы тут не очень подходят. Я уточню желательный сценарий использования ResultValues: 1) расчёт у меня работает циклически - приходит новая порция данных и класс Calc делает итерацию расчёта; 2) при каждой итерации класс Calc полностью обновляет ResultValues; 3) после каждой итерации класс Result считывает "снимок" ResultValues и выводит его на экран. Если не получается после каждой итерации считать, можно пропускать - главное, чтобы Result не считывал значения из ResultValues посередение заполнения ResultValues классом Calc. В реальности я это реализую так - класс Calc молотит в бесконечном цикле while(true), ожидая новых данных и делая паузы посредством Thread.Sleep (думаю в будущем заменить на какой-нибудь SpinLock или Wait/Pulse, но пока так), класс Result делает то же самое - while(true) и Thread.Sleep. И оба класс работают с ResultValues через расшаренный синхронизирующий объект с помощью lock(synchObject). ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 07:57 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Т. е. из-за вот этого AlexUser9872) при каждой итерации класс Calc полностью обновляет ResultValues; 3) после каждой итерации класс Result считывает "снимок" ResultValues я не вижу смысла в потокобезопасных коллекциях в моём случае. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 07:58 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987В реальности я это реализую так - класс Calc молотит в бесконечном цикле while(true), ожидая новых данных и делая паузы посредством Thread.Sleep (думаю в будущем заменить на какой-нибудь SpinLock или Wait/Pulse, но пока так), класс Result делает то же самое - while(true) и Thread.Sleep. И оба класс работают с ResultValues через расшаренный синхронизирующий объект с помощью lock(synchObject). Да, забыл уточнить, что, естественно, каждый из классов Calc и Result работает в своём потоке. Точнее, в отдельных потоках запускаются методы этих классов, ответственные, соответственно, за расчёт и за вывод результатов. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 08:01 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser9872) при каждой итерации класс Calc полностью обновляет ResultValues; ... главное, чтобы Result не считывал значения из ResultValues посередение заполнения ResultValues классом Calc. Тогда генери в Calc новый List и присваивай по окончанию, т.е. так Код: c# 1. 2. 3. 4. 5.
И не надо никаких синхронизаций. Можешь AutoResetEvent добавить для пробуждения Result ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 08:26 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Dima TИ не надо никаких синхронизаций. Можешь AutoResetEvent добавить для пробуждения Result Почему не надо? А если класс Result перебирает элементы ResultValues, а в этом время класс Calc обновляет/подменяет ResultValues? Т. е. в моём случае он обновляет данные в ResultValues, а в вашем - подменяет всю коллекцию новым объектом ResultValues. Но результат всё равно один - изменение коллекции ResultValues во время её перебора другим потоком. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 08:30 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Я ещё думал создать для ResultValues коллекцию List<List<double>> ResultValuesList И чтобы после каждого расчёта Calc добавлял в ResultValuesList результаты, а Result бы забирал только последний на текущий момент элемент - т. е. последний результат. Ну и после каждого такого забора удаление из ResultValuesList всех предыдущих коллекций результатов, чтобы память не занимали. Как такой вариант? Тогда точно синхронизаций и блокировок не понадобится. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 08:34 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987Dima TИ не надо никаких синхронизаций. Можешь AutoResetEvent добавить для пробуждения Result Почему не надо? А если класс Result перебирает элементы ResultValues, а в этом время класс Calc обновляет/подменяет ResultValues? Т. е. в моём случае он обновляет данные в ResultValues, а в вашем - подменяет всю коллекцию новым объектом ResultValues. Но результат всё равно один - изменение коллекции ResultValues во время её перебора другим потоком. Результаты разные: в твоем случае объект один, в моем случае два разных, поэтому синхронизация не нужна. Немного не дописал, чтобы в Result не сглючило лучше так Код: c# 1. 2. 3. 4.
Т.е. Calc() создал объект, наполнил, сохранил ссылку на объект в ResultValues и больше с объектом не работает. Result() взял ссылку из ResultValues и вывел. Точка пересечения Calc() и Result() только в передаче ссылки через ResultValues. Эта операция атомарная, т.е. не требует доп.синхронизаций (если путаю поправьте). ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 08:43 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987В реальности я это реализую так - класс Calc молотит в бесконечном цикле while(true), ожидая новых данных и делая паузы посредством Thread.Sleep Черезжопная реализация. Sleep() - вставка тормоза чтоб проц в холостую не гонять. Разбирайся с источником данных, чтобы он будил Calc при появлении данных. AlexUser987(думаю в будущем заменить на какой-нибудь SpinLock или Wait/Pulse, но пока так), класс Result делает то же самое - while(true) и Thread.Sleep. И оба класс работают с ResultValues через расшаренный синхронизирующий объект с помощью lock(synchObject). Используй AutoResetEvent: Result висит на эвенте, Calc как посчитал и сохранил сбрасывает эвент, Result просыпается, выводит и дальше висит на эвенте. PS Почитай Дж. Рихтер CLR via C# . Часть V. Многопоточность. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 08:46 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Dima TAlexUser987пропущено... Почему не надо? А если класс Result перебирает элементы ResultValues, а в этом время класс Calc обновляет/подменяет ResultValues? Т. е. в моём случае он обновляет данные в ResultValues, а в вашем - подменяет всю коллекцию новым объектом ResultValues. Но результат всё равно один - изменение коллекции ResultValues во время её перебора другим потоком. Результаты разные: в твоем случае объект один, в моем случае два разных, поэтому синхронизация не нужна. Немного не дописал, чтобы в Result не сглючило лучше так Код: c# 1. 2. 3. 4.
Т.е. Calc() создал объект, наполнил, сохранил ссылку на объект в ResultValues и больше с объектом не работает. Result() взял ссылку из ResultValues и вывел. Точка пересечения Calc() и Result() только в передаче ссылки через ResultValues. Эта операция атомарная, т.е. не требует доп.синхронизаций (если путаю поправьте). Да, вы правы - я упустил из виду var list = new List<double>(); в классе Calc. Спасибо и за другие советы - посмотрю. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 13:29 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987var list = new List<double>(); ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 13:30 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Не вчитывался особо в суть проблемы,уверен, что задачу можно решить проще и по другому, но ответы на вопросы на всякий случай. AlexUser987А что, если сделать само свойство ResultValues синхронизируемым объектом, и везде, где используется это свойство - неважно, в своём классе, в котором объявлено это свойство, или в чужих - писать lock(ResultValues)? SyncRoot . Как раз тот же принцип. Все уже придумано до нас. AlexUser987Был ещё вариант, сделать это свойство приватным, но добавить публинчые обёртки над этим свойством, типа GetResultValues, AddNewValuesToResult и т. п Делается обычно так. В класс добавляются 2 метода. Например, Lock и UnLock. В Lock добавляем Monitor.Enter, в UnLock сами догадаетесь? Применение: Код: plaintext 1. 2. 3. 4. 5. 6.
Но ценность таких вариантов достаточно сомнительна. В первую очередь нужно смотреть на возможность lock-free работы. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 14:21 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Arm79Не вчитывался особо в суть проблемы,уверен, что задачу можно решить проще и по другому, но ответы на вопросы на всякий случай. AlexUser987А что, если сделать само свойство ResultValues синхронизируемым объектом, и везде, где используется это свойство - неважно, в своём классе, в котором объявлено это свойство, или в чужих - писать lock(ResultValues)? SyncRoot . Как раз тот же принцип. Все уже придумано до нас. AlexUser987Был ещё вариант, сделать это свойство приватным, но добавить публинчые обёртки над этим свойством, типа GetResultValues, AddNewValuesToResult и т. п Делается обычно так. В класс добавляются 2 метода. Например, Lock и UnLock. В Lock добавляем Monitor.Enter, в UnLock сами догадаетесь? Применение: Код: plaintext 1. 2. 3. 4. 5. 6.
Но ценность таких вариантов достаточно сомнительна. В первую очередь нужно смотреть на возможность lock-free работы. Про синк рут спасибо. Про дальнейшее - что-то сомнительным выглядит. Как заставить пользователя класса пользоваться моим классом именно по такой сложной схеме? С таким же успехом можно заставить его самостоятельно локать свойства-коллекции этого класса - т. е. никак. Разве что в комментариях или документации к классу очень сильно попросить, что делать надо именно так, а не иначе, а если нет, то будет упс. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 15:55 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Про синк рут полезная вещь. Просто на самом деле я упростил свой пример и у меня по смыслу не одна коллекция пишется-читается, а несколько, поэтому выделить специальный объект под синхронизацию ВСЕГО этого дела в виде "атомарной" операции под локом кажется более подходящим. А вообще, как поступить, если в моём классе Result не просто одна коллекция, которую постоянно целиком обновляет другой класс, а нечто смешанное? Например, у меня вот такая коллеция, как выше описана - т. е. полностью обновляющаяся в каждой итерации расчёта. Потом ещё другая коллеция, которая лишь добавляет очередную пачку рассчитанных данных в конец уже существующих - т. е. пополняющиеся результаты. Плюс ещё просто числа, которые обновляются. И вот весь этот набор надо бы защищить от изменения классом расчёта Calc, пока его класс результатов Result выводит на экран. Тут удобнее всё это под одним общим локом разместить, или как? Или к каждому типу данных свой подход - где-то volatile, где-то из concurrent collection взять, а где-то через lock сделать? Я вот пока реализовал - всё изменения и чтения всех этих разношёрстных результатов поместил под одним общим lock. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 16:02 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987Я вот пока реализовал - всё изменения и чтения всех этих разношёрстных результатов поместил под одним общим lock. Работает? Проблем по скорости нет? Никто не жалуется? Значит, нормально. Сделайте хоть как то, чтобы работало, потом рефакторьте код. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 17:08 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Arm79AlexUser987Я вот пока реализовал - всё изменения и чтения всех этих разношёрстных результатов поместил под одним общим lock. Работает? Проблем по скорости нет? Никто не жалуется? Значит, нормально. Сделайте хоть как то, чтобы работало, потом рефакторьте код. Да давно уже работает. Я вот откопал своё старьё и думаю, как получше сделать. Т. к. скоро что-то подобное придётся снова делать - думаю, не повторять же то же самое. Может, получше варианты есть. Так сказать, общие подходы. Тот же Альбахари про это, вроде, ничего не пишет. В этом вашем Рихтере есть что подобное? ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 17:27 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Dima TТочка пересечения Calc() и Result() только в передаче ссылки через ResultValues. Эта операция атомарная, т.е. не требует доп.синхронизаций (если путаю поправьте). ставьте volatile и должно быть норм. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 18:03 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987В этом вашем Рихтере есть что подобное? Почитай, хуже не будет. ХЗ есть или нет, слишком уж абстрактно у тебя задача сформулирована. По крайней мере узнаешь о всех возможных альтернативах решения твоей задачи. Как выше упомянули надо в сторону lock-free идти (в моих примерах оно самое). Закон Амдала никто не отменял, поэтому чем больше синхронизируешься, тем меньше пользы от распараллеливания. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 18:04 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
AlexUser987, А каждая порция данных от Calc должна быть обработана Result? ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 18:08 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Arm79AlexUser987, А каждая порция данных от Calc должна быть обработана Result? Нет. Result делает слепок с набора результатов и потом сразу "освобождает" (в смысле, что перестаёт использовать - считывать значения там и прочее) этот набор, чтобы Calc дальше мог в него писать. В момент создания слепка я и применяю блокировку через lock. После создания слепка Result неспешно выводит результаты из этого слепка на экран. Неспешно в том смысле, что копирование данных в слепок происходит гораздо быстрее, чем отображение на экране. В этом время Calc под локом пишет в набор результатов, которые только что освободил Result. Обычно у меня получается, что Calc может множество итераций расчёта провести на одну итерацию вывода результатов - т. е. он несколько раз перезапишет набор результатов. Поэтому Result берёт слепок всегда с последней версии результатов. Это нужно, чтобы результаты отображались в реальном времени - с приемлемой задержкой по отношению к поступающим данным. Задачи показать вывод именно по всем результатам у меня пока нет. Если будет - переделаю, чтобы результаты накапливались в коллекции. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 18:45 |
|
Насчёт синхронизируемого объекта - lock(synchObject)
|
|||
---|---|---|---|
#18+
Dima T Закон Амдала Кстати, он показывает, что технологической сингулярности не будет. Даже если доведут до ума квантовые компьютеры. Но будет некоторое приближение к ней. Довольно близкое и болезненное. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.12.2015, 18:48 |
|
|
start [/forum/topic.php?fid=20&fpage=70&tid=1400964]: |
0ms |
get settings: |
9ms |
get forum list: |
13ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
42ms |
get topic data: |
15ms |
get forum data: |
3ms |
get page messages: |
58ms |
get tp. blocked users: |
1ms |
others: | 15ms |
total: | 164ms |
0 / 0 |