На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
Модераторы: Qraizer
Страницы: (16) « Первая ... 13 14 [15] 16  все  ( Перейти к последнему сообщению )  
> Thread-safe shared_ptr , Реализация потоко-безопасного SharedPtr
    Мы разве говорим не об weak_ptr?
      Цитата Олег М @
      Мы разве говорим не об weak_ptr?

      Я, честно говоря, уже затрудняюсь сказать - о чём мы говорим. С моей точки зрения беседа сейчас напоминает что-то типа:
      - Скажите, а какой микроском лучше мне взять - электронный или оптический?
      - А вам для чего?
      - Да вот пару гвоздей нужно забить.
      - Ну так возьмите молоток!
      - Не, мне не нужен молоток. Вы скажите, какой микроскоп лучше?
        Цитата OpenGL @
        Я так и не понял - почему нельзя передавать в другой поток shared_ptr тупо по значению?

        Насколько я понимаю - "тупо" нельзя, и вот почему.
        Предположим, что shared_ptr гарантированно потоко-безопасен.
        ---
        Мы пытаемся из другого потока получить от этого экземпляра shared_ptr
        указатель на объект, для работы с данными в другом потоке.
        Создавая в другом потоке ещё один экземпляр (2-й) shared_ptr.
        В момент присоединения к группе нового экземпляра shared_ptr,
        процедура из 1-го потока (вдруг) решит уничтожить 1-й экземпляр shared_ptr,
        поскольку для неё данные уже не актуальны.
        ---
        Дальше как повезёт.
        Если сначала будет получена ссылка на необходимые данные для 2-го потока,
        а потом уничтожен 1-й shared_ptr - хорошо.
        Но если наоборот - всё плохо.
        1-й был единственным в группе, он уничтожит данные, так необходимые 2-му.
        И 2-й получит NULL.
        В результате получаем вероятностный исход работы программы.
          С моей точки зрения выглядит по другому. Я вам говорю – вот есть целый класс задач, для которых есть общее решение. Вы мне отвечаете – а зачем нам общее решение, если мы все эти задачи можем решить по–своему?
            Цитата Олег М @
            С моей точки зрения выглядит по другому. Я вам говорю – вот есть целый класс задач, для которых есть общее решение. Вы мне отвечаете – а зачем нам общее решение, если мы все эти задачи можем решить по–своему?

            Я пока не увидел "целого класса задач". Я увидел две задачи, одна про publisher/subscriber и управления коллекцией подписчиков, другая - что-то вроде producer/consumer с генерацией и обработкой "жирных" объектов. В обоих случаях "общее решение" предполагало скрещивание двух контрактов, которые обычно не скрещивают, и которые, в общем, и не стоит скрещивать - проблему можно решить иначе и проще.
              Язык так и чешется написать что–нибудь по поводу скрещивания.
              Но, одна из этих задач решается в разы эффективнее, вторая не требует никаких дополнительных средств синхронизации.
              И таких задач десятки, если не сотни.
                Цитата Олег М @
                Но, одна из этих задач решается в разы эффективнее, вторая не требует никаких дополнительных средств синхронизации.

                А позволь уточнить: в чём эффективность решения заключается? Ты работаешь с разделяемым ресурсом, но при этом делаешь вид, что не работаешь с разделяемым ресурсом, а просто ссылочками щёлкаешь. Вроде бы выглядит красиво, по факту - скрытые контракты, смешение контрактов и неявность. Что мешает поступить так, как я показал (с "колхозным" объектом и явной синхронизацией) - я так и не понял. Во втором случае... Та же самая ситуация. Ты пытаешься сэкономить на синхронизациях там, где ты явно предполагаешь тяжёлые и длинные операции. Если запустишь под профайлером, то сможешь увидеть, что даже лочка на мьютексе будет незаметна рядом с аллокациями, форматированием и всем прочем. И опять возвращаемся к сакраментальному: чего лечим то? Какую проблему решаем? Или оптимизируем ради оптимизации?
                  Цитата Flex Ferrum @
                  Ты работаешь с разделяемым ресурсом, но при этом делаешь вид, что не работаешь с разделяемым ресурсом, а просто ссылочками щёлкаешь. Вроде бы выглядит красиво, по факту - скрытые контракты, смешение контрактов и неявность.

                  А ты не заметил, что много с какими ресурсами работаешь таким образом? Например вызывая оператор new?
                    Цитата Олег М @
                    Цитата Flex Ferrum @
                    Ты работаешь с разделяемым ресурсом, но при этом делаешь вид, что не работаешь с разделяемым ресурсом, а просто ссылочками щёлкаешь. Вроде бы выглядит красиво, по факту - скрытые контракты, смешение контрактов и неявность.

                    А ты не заметил, что много с какими ресурсами работаешь таким образом? Например вызывая оператор new?

                    Нет. Не заметил. Во избежание выходных с дебаггером за две недели до релиза я предпочитаю явные контракты - скрытым, тем более в отношении разделяемых между потоками ресурсов. :)
                      Цитата Flex Ferrum @
                      Нет. Не заметил. Во избежание выходных с дебаггером

                      Зря не заметил. Кстати, я и будни с ним не провожу, вообще не припомню когда пользовался.
                      Отвлеклись от темы
                        Цитата Олег М @
                        Зря не заметил. Кстати, я и будни с ним не провожу, вообще не припомню когда пользовался.

                        Собственно, почему "баба яга против" и в чём она видит проблему такого мегаполезнейшего примитива. Разделяемые ресурсы в многопоточной среде - это потенциальные источники гонок. Даже если код пишется экспертами, обкладывается тестами и всё такое прочее, гонки всё равно возникают. В моей практике ещё не было такого, чтобы без них обошлось в сложной многопоточной программе. Твой примитив типа "решает проблему". Он прячет фактический обмен между потоками под ковёр. Один поток типа что-то записал, другой - что-то подхватил, тра-ля-ля, вуа-ля. Никакого уведомления о том, что содержимое объекта поменялось, он умер или ещё чего-то, никаких синхронизаций. Просто поток работал-работал с одной копией а потом "хоп", и стала другая. Наверное это удобно. Для любителей "написал и забыл". Но именно сокрытие факта передачи объекта между потоками, неочевидность (из кода) этого механизма - это то, что называется error prone. Плюс ко всему нарушение принципа single responsibility. В итоге я не могу признать такой контракт и такой хелперный класс полезным.
                          Ты о чём вообще, какие гонки, какая неочевидность?

                          Блокировки делать надо в любом случае.
                          Чем они проще, тем лучше. Вложенных блокировок по-возможности лучше избегать.
                          В некоторых сценариях блокировки обеспечивать сложно.

                          В случае с shared_ptr/weak_ptr - блокировать здесь нужно указатель на объект. Этот указатель разделяется разными экземплярами классов.
                          Поэтому разумно предположить, что этот указатель должен сопровождать экземпляр мьютекса (грубо говоря), тоже один для всех.
                          Вместо мьютекса здесь используется счетчик. Но смысл его работы тот же - пока он заблокирован, никто не может удалить объект.
                          Так как доступ к этому счётчику осуществляется из разных потоков, то разумно сделать, чтобы его методы были атомарными.
                          Как ты будешь обеспечивать эту атомарность - с помощью мьютекса, либо с помощью спинлоков неважно, дело вкуса. Но обеспечивать её нужно.

                          Таким образом единственное, что ты здесь блокируешь - это изменение переменной, типа uintmax_t.
                          Ты можешь показать более оптимальный сценарии блокировки?
                          Сообщение отредактировано: Олег М -
                            Цитата Олег М @
                            Блокировки делать надо в любом случае.
                            Чем они проще, тем лучше. Вложенных блокировок по-возможности лучше избегать.
                            В некоторых сценариях блокировки обеспечивать сложно.

                            В случае с shared_ptr/weak_ptr - блокировать здесь нужно указатель на объект. Этот указатель разделяется разными экземплярами классов.
                            Поэтому разумно предположить, что этот указатель должен сопровождать экземпляр мьютекса (грубо говоря), тоже один для всех.
                            Вместо мьютекса здесь используется счетчик. Но смысл его работы тот же - пока он заблокирован, никто не может удалить объект.
                            Так как доступ к этому счётчику осуществляется из разных потоков, то разумно сделать, чтобы его методы были атомарными.
                            Как ты будешь обеспечивать эту атомарность - с помощью мьютекса, либо с помощью спинлоков неважно, дело вкуса. Но обеспечивать её нужно.

                            Таким образом единственное, что ты здесь блокируешь - это изменение переменной, типа uintmax_t.
                            Ты можешь показать более оптимальный сценарии блокировки?

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

                            Добавлено
                            И да. С его использованием weak_ptr не нужен. Этот объект (по сути) выполняет функции этого самого weak pointer'а в рамках обсуждаемых задач.
                              Цитата Flex Ferrum @
                              Я чуть выше привёл пример кода, который реализует всё то же самое, только в профиль. Та же лочка (на спинлоке), только локальная, для каждого объекта своя, тот же контролируемый доступ к указателю на объект.

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

                              Цитата Flex Ferrum @
                              Смарт-указатель лежит внутри объекта и предоставляется по требованию, его счётчики ссылок щёлкаются, когда требуется, всё контролируемо (ну, за исключением создания копии самого объект


                              Смарт-указатель - это класс, который хранит указатель на объект и в деструкторе вызывает функцию удаления для этого объекта. Всё остальное - это твои фантазии.

                              Цитата Flex Ferrum @
                              И да. С его использованием weak_ptr не нужен. Этот объект (по сути) выполняет функции этого самого weak pointer'а в рамках обсуждаемых задач.


                              Не то что бы не нужен. Скорее всего - не рекомендуется использовать.
                              Этот объект - всего лишь shared_ptr c мьютексом. Который даже скопировать нельзя.
                                Наткнулся тут на функции типа rcu_read_lock. Думаю заменить ими static CSharedPtrLock. Кто-нибудь может сказать что-то про них?
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0582 ]   [ 17 queries used ]   [ Generated: 24.04.24, 09:57 GMT ]