Как обеспечить в будущем потокобезопасный параллельный доступ к std::shared_ptr std::unique_ptr

#c #multithreading #shared-ptr #c 20 #unique-ptr

Вопрос:

Каковы некоторые рекомендуемые стратегии для проверки в будущем современного кодирования на C параллельного доступа к std::shared_ptr (подобным) и std::unique_ptr (подобным) структурам данных по мере развития спецификации языка C в этой области?

Фон:

Примерно в 2021 году доступные конструкции языка C для управления доступом к std::shared_ptr (подобным) и std::unique_ptr (подобным) интеллектуальным указателям с помощью удобных для параллелизма способов находятся в процессе разработки. Например, поддержка C 20 для std::atomiclt;std::shared_ptrgt; еще не очень продвинулась в компиляторе в дикой природе, но спецификация C 20 говорит нам, что она грядет.

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

Кто-нибудь знает хорошую стратегию (возможно, хороший набор макросов CPP?) для проверки в будущем современного кода параллелизма?

[ УТОЧНЯЮЩЕЕ РЕДАКТИРОВАНИЕ после нескольких хороших комментариев (Спасибо всем) ] Из того, что я понял:

  1. Разные экземпляры одного std::shared_ptr и std::unique_ptr могут быть прочитаны/записаны, из разных потоков без проблем, (например, когда разные экземпляры передаются в различные потоки), но для экземпляра объекта (или памяти), на которые они указывают, не могут быть безопасно доступны из нескольких потоков одновременно (так что вы должны использовать мьютекс, или другой способ получить доступ к заостренной к объекту, если это сценарий использования). [ Спасибо Алексу Гутеневу за эту ясность ]
  2. Один и тот же экземпляр из std::shared_ptr или std::unique_ptr не может быть прочитано/записано на нити безопасным способом, используя (до c 20: std::atomic_load/store и т. д., И ПОСЛЕ С 20: std::atomiclt;std::shared_ptrgt; или std::atomiclt;std::unique_ptrgt; ) я думаю, что это может быть место, чтобы использовать cpp макросы, такие как SHARED_GET, SHARED_SET, UNIQUE_GET, UNIQUE_SET что бы централизовать изменения нужно сделать, чтобы перейти от C 17 до c 20. [ NicolBolas спасибо за ясность о том, что на самом деле в C 20. Как было указано: ссылка, которую я предоставил в комментариях ниже, устарела, поэтому будьте осторожны, чтобы не считать это фактом.]
  1. Если вы не проходящий std::unique_ptr между потоками, используя std::move пройти указал в памяти, и с помощью очередей для применения, что только один поток имеет доступ в любой момент времени, вы можете использовать std_unique указатели сами и отметил в памяти в поток, который получает указатель без мьютексов и других средств защиты против ресурс утверждение.

Из-за моего замешательства при задании мой первоначальный вопрос, возможно, сбивал с толку. Теперь я бы перефразировал вопрос так: я ищу набор макросов access CPP #определяет, которые определяют C 17 и C 20 и используют самое чистое/правильное определение этой версии для следующих операций:

  • MAKE_LOCAL_SHARED: Создайте/загрузите локальный экземпляр std::shared_ptr из общего/общего экземпляра, который поток может читать/записывать без разногласий с оригиналом. Он должен указывать на ту же память, на которую указывала общая/общая
  • BEGIN_USE_SHARED_TGT: Создайте хранилище a std::lock_guard/mutex , в пределах которого можно безопасно использовать указанный в памяти локальный std::shared_ptr экземпляр.
  • END_USE_SHARED_TGT: (возможно, просто закрывающая скобка?) Отпустите std::lock_guard/mutex , когда закончите, используя указатель на память
  • BEGIN_USE_UNIQUE_TGT, END_USE_UNIQUE_TGT (то же, что и выше для std::unique_ptr

Комментарии:

1. Вы можете использовать псевдоним шаблонного типа, который либо ссылается на std::atomiclt;std::shared_ptrlt;Tgt;gt; то, поддерживается ли он, либо на альтернативное решение для блокировки или для конкретной платформы, если это не так. Просто убедитесь, что ваша альтернатива совместима с интерфейсом std::atomiclt;std::shared_ptrlt;Tgt;gt; .

2. Я бы предположил, что будущий C будет поддерживать обратную совместимость с текущими требованиями к этим объектам для обеспечения потокобезопасности, если только модель потоковой передачи не изменится таким образом, что будет невозможно обеспечить обратную совместимость.

3. @Dave: » Конструкции языка C для управления доступом к интеллектуальным указателям std::shared_ptr(-like) и std::unique_ptr(-like) в режиме параллелизма находятся в процессе разработки». Нет, это не так. atomiclt;shared_ptrgt; это просто более приятная оболочка вокруг того, что уже разрешено в C 11. И даже в этом случае это не имеет значения, потому atomiclt;shared_ptrgt; что речь идет о том, чтобы разрешить нескольким потокам иметь атомарный доступ к определенному shared_ptr объекту, а не к объекту, на который он указывает. Это отличается от простой передачи a shared_ptr в другой поток.

4. @NicolBolas, atomiclt;shared_ptrgt; — это просто более приятная оболочка вокруг того, что C 11 уже разрешилlt;shared_ptrgt; — не обязательно, так atomiclt;shared_ptrgt; как может быть заблокирован. Но в остальном согласен

5. @Дейв: Нет никакого atomic_shared_ptrlt;Typegt; типа. Это сообщение было с 2017 года, и в нем говорилось о ранней версии предложения. Комитет принял это предложение и переименовал тип atomiclt;shared_ptrlt;Tgt;gt; , чтобы было ясно, что это такое.

Ответ №1:

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

При использовании очереди (без блокировки) вы не получаете доступ к произведенным и потребленным элементам одновременно.

Для доступа к различным переменным unique_ptr и shared_ptr уже безопасны. Когда два shared_ptr s указывают на один и тот же объект, существует гарантия того, что управление этими shared_ptr s в разных потоках является потокобезопасным, обычно реализуемым с использованием подсчета ссылок. Разные unique_ptr буквы s не указывают на один и тот же объект.

Просто используйте shared_ptr и unique_ptr , как обычно, если вы просто помещаете их в очередь и на самом деле не получаете доступ к одной и той же переменной из нескольких потоков одновременно.

Комментарии:

1. Спасибо, что указали на это! Первоначально я использовал строгий подход ТОЛЬКО для unique_ptr, используя очереди 1×1, поэтому было совершенно очевидно, какой поток имел доступ к unique_ptr и на что он указывал. Это было в значительной степени потому, что я не совсем уверен, что понимаю все различные общие/уникальные/атомарные комбинации (см. Комментарий к Николболасу выше для разных вкусов). Что я хотел бы сделать, так это ослабить требование к копированию unique_ptr, когда я хочу отправить его нескольким потокам последующей обработки. Я хотел бы превратить его в shared_ptr (избежать копирования) и отправить его дальше.

2. Мой вопрос о проверке в будущем заключается в том, чтобы централизовать любой шаблон, который я реализую, чтобы его можно было обновить, когда появится C 20, но только в том случае, если мне действительно понадобятся эти новые типы. Прежде чем наступит будущее, я все равно должен разобраться в настоящем! 🙂

3. @Дейв, ты можешь завернуть templatelt;Tgt; using SharedPtr = std::share_ptrlt;Tgt; , если хочешь. Но я не думаю, что в этом есть необходимость. std::atomiclt;std::shared_ptrlt;Tgt;gt; это не замена std::shared_ptrlt;Tgt; , а скорее замена std::shared_ptrlt;Tgt; защищенного мьютексом.