Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
execute_around<std::vector<int>> vecc();
auto transaction = vecc.begin_transaction();
for (int i = 0; i < 100; ++i)
{
vecc->push_back(i);
}
safe_ptr<std::vector<int>> vecc;
{
std::lock_guard<decltype(vecc)> lock(vecc); // или lock_timed_any_infinity lock(vecc)
for (int i = 0; i < 100; ++i)
{
vecc->push_back(i);
}
}
А если все операции такого цикла выполняются без пессимистических блокировок, то такой алгоритм называется lock-free.Немного странное утверждение, алгоритм/структура данных является lock-free, если он/она предоставляет определенные гарантии прогресса, а не только, если в ней отсутствует использование блокировок. Таким образом, то что вы пишите не совсем согласуется с понятием lock-free:
std::safe_ptr<std::queue<int>> queue_ptr;
{
std::lock_guard<decltype(queue_ptr)> lock(queue_ptr);
int item = queue_ptr->front();
queue_ptr->pop();
}
res1 = f(vecc1->a(), vecc2->a());
res2 = f(vecc2->a(), vecc1->a());
lock_timed_any_infinity lock(vecc1, vecc2); // причем не важно в каком порядке они здесь идут
res1 = f(vecc1->a(), vecc2->a());
res2 = f(vecc2->a(), vecc1->a());
lock_timed_any_infinity lock(vecc2, vecc1); // причем не важно в каком порядке они здесь идут
res1 = f(vecc1->a(), vecc2->a());
res2 = f(vecc2->a(), vecc1->a());
typedef execute_around<Foo> T;
{
T::proxy tmp1(vecc1.p.get(), *vecc1.mtx);
T::proxy tmp2(vecc2.p.get(), *vecc2.mtx);
res1 = f(tmp1.p->a(), tmp2.p ->a());
}
{
T::proxy tmp1(vecc2.p.get(), *vecc2.mtx);
T::proxy tmp2(vecc1.p.get(), *vecc1.mtx);
res2 = f(tmp2.p->a(), tmp1.p ->a());
}
Т.е. функции move_money() и show_user_money_on_time() не были завершены и остановились навечно в deadlock. Решения есть 4:
std::lock(mutex1, mutex2);
// critical section
mutex1.unlock();
mutex2.unlock();
{
std::lock(mutex1, mutex2);
std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
// critical section
}
{
lock_timed_any_infinity lock(safe_ptr1, safe_ptr2);
// critical section
}
§ 30.4.3 Generic locking algorithms
(5)
Effects: All arguments are locked via a sequence of calls to lock(), try_lock(), or unlock() on each
argument. The sequence of calls shall not result in deadlock, but is otherwise unspecified. [ Note: A
deadlock avoidance algorithm such as try-and-back-off must be used, but the specific algorithm is not
specified to avoid over-constraining implementations. — end note ] If a call to lock() or try_lock()
throws an exception, unlock() shall be called for any argument that had been locked by a call to
lock() or try_lock().
Мне показалось, или у вас действительно не определены операторы копирования/присвоения для safe_ptr?
Без них (точнее с автоматическими версиями) он сам по себе потоконебезопасен и полезность его очень ограничена.
const std::shared_ptr<T> ptr;
, которая неявно удаляет operator=().safe_ptr<int> safe_ptr1 = 1; // OK
safe_ptr<int> safe_ptr2 = 2; // OK
safe_ptr<int> safe_ptr3 = safe_ptr2; // OK
safe_ptr1 = safe_ptr2; // error: operator=() is implicitly deleted because the default definition would be ill-formed
В части враппера над мапой - есть ли большой смысл 2 разных внутренних класса создавать (auto_lock_t и auto_lock_obj_t) ?
Почему не:
template<typename T, typename mutex_t = std::recursive_mutex, typename x_lock_t = std::unique_lock<mutex_t>,
typename s_lock_t = std::unique_lock<mutex_t >>
class safe_ptr {
typedef mutex_t mtx_t;
const std::shared_ptr<T> ptr;
std::shared_ptr<mutex_t> mtx_ptr;
template<typename req_lock>
class auto_lock_t {
T * const ptr;
req_lock lock;
public:
auto_lock_t(auto_lock_t&& o) : ptr(std::move(o.ptr)), lock(std::move(o.lock)) { }
auto_lock_t(T * const _ptr, mutex_t& _mtx) : ptr(_ptr), lock(_mtx){}
T* operator -> () { return ptr; }
const T* operator -> () const { return ptr; }
template<typename arg_t>
auto operator [] (arg_t arg) -> decltype((*ptr)[arg]) { return (*ptr)[arg]; }
};
void lock() { mtx_ptr->lock(); }
void unlock() { mtx_ptr->unlock(); }
friend struct link_safe_ptrs;
template<typename mutex_type> friend class std::lock_guard;
//template<class... mutex_types> friend class std::lock_guard; // C++17
public:
template<typename... Args>
safe_ptr(Args... args) : ptr(std::make_shared<T>(args...)), mtx_ptr(std::make_shared<mutex_t>()) {}
auto_lock_t<x_lock_t> operator -> () { return auto_lock_t<x_lock_t>(ptr.get(), *mtx_ptr); }
auto_lock_t<x_lock_t> operator * () { return auto_lock_t<x_lock_t>(ptr.get(), *mtx_ptr); }
const auto_lock_t<s_lock_t> operator -> () const { return auto_lock_t<s_lock_t>(ptr.get(), *mtx_ptr); }
const auto_lock_t<s_lock_t> operator * () const { return auto_lock_t<s_lock_t>(ptr.get(), *mtx_ptr); }
};http://coliru.stacked-crooked.com/a/19a0a524c899a52e
Делаем любой объект потокобезопасным