cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

Потокобезопасный умный указатель #381

Open Neargye opened 3 years ago

Neargye commented 3 years ago

Перенос предложения: голоса +10, -2 Автор идеи: valera_ee

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

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

К примеру статья на хабре https://habr.com/ru/post/328348/

Имеено за простоту и удобство использование мне это решение очень нравится, хотя оно не лишено недостатков. Довольно элегантно выглядит решение:

thread_safe_ptr<Object> p = make_thread_safe_ptr<Object>();

void func_thread1(thread_safe_ptr<Object> p) 
{
    // выполнение в потоке 1
    p->process();
}

void func_thread2(thread_safe_ptr<Object> p) 
{
    // выполнение в потоке 2
    p->process();
}
Neargye commented 3 years ago

yndx-antoshkka 4 апреля 2019, 15:38 Нечто подобное есть в Boost https://www.boost.org/doc/libs/1_69_0/doc/html/thread/sds.html

Идея интересная, если готовы взяться - стоит проверить что подобные идеи раньше не предлагались и начать писать предложение.

valera_ee 4 апреля 2019, 22:04 yndx-antoshkka, Чтобы убедиться в работоспособности я сделал простенький вариант с простым тестом (проект на Qt 5): https://github.com/ValeraMikhaylovsky/thread_safe_ptr

Если идея вам покажется интересной, то готов услышать критику по реализации и продолжить работу в этом направлении.

valera_ee 4 апреля 2019, 22:14 Посмотрел на boost::synchronized_value , идея таже, но реализация, на мой взгляд, не самая удобная. В реализации boost нужно вручную прописывать блокировку через RAII boost::strict_lock_ptr, по запарке можно и забыть, особенно если над проектом коллектив трудится. В моём варианте блокировка выполняется автоматически при доступе к ресурсу. Я не являюсь автором этой идею, реализацию увидел на хабре: https://habr.com/ru/post/184436/ https://habr.com/ru/post/328348/

valera_ee 5 апреля 2019, 7:20 valera_ee, Более внимательно посмотрел почему используют RAII в boost, проверил свой указатель на dedadlock и получил его при следующем коде:

thread_safe_ptr<Object> pObject(std::make_shared<Object>());

pObject->setValue(3);

if (pObject->valid() && pObject->method()) {
        // to do
}   

Но, конкретно в этом случае, если модифицировать код в стиле с++17, deadlock не происходит:

if (bool t_ok = pObject->valid(); t_ok && pObject->method()) {
    // to do
}

valera_ee 5 апреля 2019, 8:33 Повторил тест на deadlock для варианта sf::safe_ptr ( https://habr.com/ru/post/328348/ ), там эта проблема решена.

valera_ee 5 апреля 2019, 13:33 Решение проблемы выполнено через рекурсивный мьютекс, скорость, конечно, не большая, но вариант рабочий, для ускорения можно рекурсивный вариант спинлока заиспользовать.

Олег Власов 5 апреля 2019, 11:54 Вроде в С++20 хотят добавить atomic_shared_ptr

cppreference.com

valera_ee 5 апреля 2019, 13:35 Олег Власов, Я читал, но если я правильно понял, там доступ не к ресурсам, а к методам сомого shared_ptr будет потокобезопасным, если не прав поправьте меня.

Олег Власов 10 апреля 2019, 19:33 valera_ee, Я поподробнее прочитал про atomic_shared_ptr. Ты прав, он защиает сам shared_ptr, а не то на что указывает.

valera_ee 7 апреля 2019, 16:05 Обновил код на github как самого указателя так и теста, в текущем исполнении меня полностью устраивает поведение умного указателя. Хотелось бы услышать мнение разработчиков, которые более глубоко разбираются в теме многопоточного программирования.

LevSch 8 августа 2019, 17:10 Основной его недостаток в том, что между двумя вызовами методов, инвариант объекта может измениться. И блокировать на каждом методе медленно, если несколько методов подряд вызывается.