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

    А вот это всё от конкретных сценариев зависит. shared/weak_ptr - это сценарий, когда свет гасит последний уезжающий. Т. е. может быть сколько угодно ссылок на объекты в разных местах программы, когда последняя отваливается - объект удаляется. Удобно. Но не всегда. У тебя, если я всё правильно понимаю, вопросов с тем, когда именно создавать и удалять объект - нет. Создаётся объект тогда, когда совершается коннект к серверу, удаляется - при дисконнекте, ну или вскоре после. То есть создание/удаление неразрывно связано с состоянием определённого ресурса. Поэтому в этом случае возникает совсем другой вопрос: можно ли в данный конкретным момент пользоваться объектом? Именно поэтому ты пытаешься пользовать связку shared_ptr/weak_ptr. Ну, как я понимаю.

    Цитата Олег М @
    Т.е. ты не смог написать такой класс?

    Конкретно такой - нет. И даже причину назвал: его варианты использования не соответствуют требуемым тобою сценариям. А то, что соответствует, не является shared/weak_ptr'ом.
      Цитата Flex Ferrum @
      . У тебя, если я всё правильно понимаю, вопросов с тем, когда именно создавать и удалять объект - нет. Создаётся объект тогда, когда совершается коннект к серверу, удаляется - при дисконнекте, ну или вскоре после. То есть создание/удаление неразрывно связано с состоянием определённого ресурса.


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

      Цитата Flex Ferrum @
      Поэтому в этом случае возникает совсем другой вопрос: можно ли в данный конкретным момент пользоваться объектом?

      Пользоваться - т.е. обращаться к содержимому указателя? До, но только в том случае, когда ты владеешь shared_ptr.


      Цитата Flex Ferrum @
      Конкретно такой - нет. И даже причину назвал: его варианты использования не соответствуют требуемым тобою сценариям. А то, что соответствует, не является shared/weak_ptr'ом.


      Не соответствует в чём? Интерфейс вроде такой же, поведение тоже. Отличается только реализация.
        Цитата Олег М @
        Не соответствует в чём?

        Не соответствует хотя бы тем, что тебе необходимо из разных потоков работать с одной копией сильного/слабого указателя. Просто вот этой вот самой необходимостью. Это означает, что ты не можешь раздать по потокам копии указателей на некую сущность и "забыть" про неё. Это означает, что несколько потоков контролируемо разделяют доступ к этой сущности, включая время жизни. Повторюсь: иначе всей этой возни бы не было. Попробуй написать хотя бы код своего примера так, чтобы он работал с таким вариантом объявления лямбд:
        ExpandedWrap disabled
          threads.emplace_back(std::make_unique<std::thread>([sp, sp2, &stop]()
          // ...
           
          threads.emplace_back(std::make_unique<std::thread>([sp2, &stop]()
          // ...

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

          Это вопрос реализации класса, таких требований к нему нет. Видел, что я выше писал по поводу msdn?


          Цитата Flex Ferrum @
          threads.emplace_back(std::make_unique<std::thread>([sp2, &stop]()
          // ...

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

          sp2 - это вроде weak_ptr. И что с ним делать во втором потоке, как не вызывать lock()?
            Таки попробуй переписать свой пример так, чтобы каждый поток использовал свою собственную копию указателя. Когда не получится - попробуй объяснить, почему не получилось, и зачем нужно иначе.
              Смысл примера, в том, что один поток изменяет указатель, а второй делает с него копию. Собственно, это и есть то, для чего и сделаны эти классы. Что там переписывать?

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

                Нет. Они не сделаны для этого. В том то и фишка. :) Согласно контракту и идеи shared_ptr'а (любой его реализации) reset/изменение одного указателя (его копии) не влияет на другие. А вот когда тебе понадобилось, чтобы влияло, ты и начал городить огород. Но этот огород - уже не shared_ptr.
                Представим себе такую ситуацию:
                ExpandedWrap disabled
                  std::string *sp;
                  std::string **sp2;
                   
                  //CSharedPtr<std::string> sp;
                  //CWeakPtr<std::string> sp2;
                   
                  volatile bool stop = false;
                  std::list<std::unique_ptr<std::thread>> threads;
                  threads.emplace_back(std::make_unique<std::thread>([&sp, &sp2, &stop]()
                  {
                      while(!stop)
                      {
                          sp = new std::string("!!!!!!!!!!!!!!!!!!!!!!!!!!!!11111111111111111111111111111111111111";
                          *sp2 = sp;
                      }
                  }));
                   
                  threads.emplace_back(std::make_unique<std::thread>([&sp2, &stop]()
                  {
                      int i = 0;
                      while(!stop)
                      {
                          auto sp = *sp2;
                          if (sp)
                              std::cout << (++i) << ", " << *sp << std::endl;
                   
                          std::this_thread::sleep_for(10ms);
                      }
                  }));
                   
                  WaitStop();
                  stop = true;
                   
                  for (auto &item: threads)
                      item->join();

                Я убрал из твоего кода смарт-указатели, заменив их на обычные. Этот код корректен? Нет. Ты работаешь с разделяемым ресурсом (sp/sp2) без синхронизации. Ты имеешь здесь data race. unique_ptr/shared_ptr/weak_ptr/XXXPtr - все они имеют семантику указателя. Они ведут себя, как обычный указатель, by design. Соответственно, любая несихронизированная работа с ними из разных потоков - это нарушение контракта и семантики. Ну вот так вот. :-? А тебе нужно именно это - чтобы изменения, сделанные одним потоком, были "подхвачены" другим. Ты хочешь, чтобы твоя обёртка над указателем по контракту позволяла такое использование. Значит, тебе нужно написать именно такую обёртку. Но это уже не будет контракт shared_ptr'а.

                Добавлено
                Цитата Олег М @
                С таким же успехом можно сказать - сделай так, чтоб каждый поток использовал свою копию мьютекса

                Вот. До тебя начинает доходить. У тебя есть разделяемый ресурс, от которого ты не можешь избавиться. И ты через этот ресурс обмениваешься данными между потоками. Значит, и контракт "обёртки" должен быть на это рассчитан. Не надо натягивать сову на глобус - ей больно. :)
                  Задачи которые решают мьютексы и shared_ptr абсолютно симметричные. Разница состоит в том, что один блокирует кусок кода от доступа, второй блокирует объект от удаления. Больше принципиальных отличий я не вижу.
                  Сообщение отредактировано: Олег М -
                    Цитата Олег М @
                    Задачи которые решают мьютексы и shared_ptr абсолютно симметричные. Разница состоит в том, что один блокирует кусок кода от, второй блокирует объект от удаления. Больше принципиальных отличий я не вижу.

                    Это как с тем сусликом. Отличий ты, может, и не видишь. А они есть. Они в том, что mutex - это (по сути и дизайну) разделяемый между потоками ресурс. Ты имеешь в разных потоках одну и ту же копию мьютекса и работаешь именно с этой копией. А shared_ptr разделяет владение неким ресурсом между разными точками в программе. Сколько точек владения (копий shared_ptr'а) - такова величина счётчика. Но при этом (в отличие от мьютекса) точки владения работают независимо друг от друга. Представь, что ты работаешь не с shared_ptr, а с IUnknown и CComPtr. Объект, с интрузивным счётчиком. Каждый новый указатель на него (по контракту) должен счётчик увеличивать, разрушение указателя - уменьшать. Объект умрёт тогда, когда количество ссылок на него - 0. И это произойдёт в любом случае, сколько бы указателей (безссылочных) на него бы не висело. COM-объекты сами следят за временем своей жизни, на желания клиентов что-то там сделать в одном потоке и прочитать в другом им, в сущности, наплевать. Пришло время умирать - умерли. shared_ptr - то же самое, только счётчик экструзивный. Опять не ясно? Разделение владения между копиями указателя != разделение ресурса между потоками. И не может быть равно.
                    Сообщение отредактировано: Flex Ferrum -
                      Цитата Flex Ferrum @
                      Это как с тем сусликом. Отличий ты, может, и не видишь. А они есть. Они в том, что mutex - это (по сути и дизайну) разделяемый между потоками ресурс. Ты имеешь в разных потоках одну и ту же копию мьютекса и работаешь именно с этой копией. А shared_ptr разделяет владение неким ресурсом между разными точками в программе


                      CSharedPtr это тоже разделяемый между потоками ресурс. Почему вдруг нет? Попробуй написать реализацию мьютекса, рекурсивного, - увидишь, что она будет очень похожа на реализацию CSharedPtr.
                        Цитата Олег М @
                        CSharedPtr это тоже разделяемый между потоками ресурс. Почему вдруг нет?

                        Так а тебе что нужно то на самом деле? Разделяемый между потоками ресурс или разделяемый между точками владения контроль времени жизни? Судя по всему - первое. А ты топишь за второе. :)
                          Цитата Flex Ferrum @
                          Разделяемый между потоками ресурс или разделяемый между точками владения контроль времени жизни?


                          Вместо "или" поставь "и", тогда будет правильно. Кстати, довольно распространённая ошибка.
                            Цитата Олег М @
                            Вместо "или" поставь "и", тогда будет правильно. Кстати, довольно распространённая ошибка.

                            В таком случае возвращаемся к:
                            Цитата Flex Ferrum @
                            Тебе нужен что-то в стиле RemoteObjectHolder. Это не столько указатель, сколько сервисный класс, контролирующий доступ к объекту из разных потоков, его операбельность и прочие подобные фишки.

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


                              Вроде ж решил. CSharedPtr - это не ресурс, это указатель на ресурс. Что за элементы дизайна? Конструктор копирования и weak_ptr::lock это не они?
                                Цитата Олег М @
                                Вроде ж решил. CSharedPtr - это не ресурс, это указатель на ресурс. Что за элементы дизайна? Конструктор копирования и weak_ptr::lock это не они?

                                Ну так у тебя "указатель на ресурс" и является разделяемым ресурсом (такой вот каламбур), точкой обмена данными, посредством которой некий объект попадает из одного потока в другой.
                                1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0502 ]   [ 15 queries used ]   [ Generated: 11.05.24, 09:18 GMT ]