我在使用互斥锁进行访问控制的容器中有对象。我经常这样做:
rsrc *r;
{
scoped_lock l(mtx);
r = container.pop( );
}
// ... use r
(我使用scoped_lock来确保在异常之后清理等等)但是,我不喜欢{...}块不是显式控制结构的一部分(它只是为scoped_lock创建一个范围) )我不喜欢 r 的空初始化,后面跟着{...}块中的(可能但不确定)赋值。
我可以这样做:
inline rsrc *locked_pop( mutex &mtx, container &c ) { scoped_lock l(mtx); return c.pop( ); }
然后
rsrc *r = locked_pop( mtx, container );
这没关系,但我需要在相同的锁下从同一个(或不同的)容器中获取多个项目。
你知道一种优雅,一般的做法吗? (这不是一个特别的Boost问题,但是我正在使用这些库,所以Boost-ism会很好。)
答案 0 :(得分:1)
std::tuple<resource_a, resource_b> lock_and_pop(std::mutex& m, container& c)
{
std::lock_guard<std::mutex> lock(m);
auto& top = container.front();
auto result = std::make_tuple(std::move(top.res_a), std::move(top.res_b));
container.pop_front();
return result;
}
答案 1 :(得分:1)
你可以使用lambdas。
template<class F>
decltype(auto) while_locked(mutex& m,F&&f){
auto lock = scoped_lock(m);
return std::forward<F>(f);
}
用过:
auto* r = while_locked(mtx, [&]{return container.pop( );});
现在,我喜欢将锁定绑定到锁定的数据。所以我写了thread_safe<T>
,其中公开了read(F&&)const
和write(F&&)const
以及-> const
。
他们的签名属于[T]->(T->U)->U
,其中[T]
是包裹的T
,(T->U)
是传入的F
,U
是F
返回的内容。
读取获取读锁定,写入写锁定,->
返回一个读锁定保持对象,其中重载->
运算符返回T const*
。
我们还可以通过对互斥锁进行多次访问来处理。
然后我们得到:
auto* r = container.write([](auto&&container){return container.pop_back();});
有点光滑。
->
技巧依赖于a->b
被定义为(a.operator->())->b
非指针类型的事实。所以我们可以返回一个锁定暂存,然后有一个返回指针的operator->
。
这允许我们执行bool b = container->empty()
,它无缝地执行读锁定,读取empty
的值,存储它,然后解锁读锁定。
答案 2 :(得分:0)
没有。如果要锁定资源并避免在每条指令中将其锁定。最好是本地访问资源,如您的问题所示。
答案 3 :(得分:0)
我没有意识到scoped_lock有一个unlock()函数,可以在销毁scoped_lock之前解锁(我以为~scoped_lock()是解锁的唯一方法,而任何其他unlock()都会导致双重 - 当scoped_locker超出范围时 - 解锁。由于你可以在范围结束之前解锁(),这对我来说足够优雅:
void consume_resource( locked_container &c ) {
scoped_lock l(c.mtx);
rsrc *r1 = c.pop( );
rsrc *r2 = c.pop( );
l.unlock( );
// CONTAINER NOT LOCKED ANY MORE, DON'T FIDDLE WITH IT (even to check empty(), etc.)
// use resources pulled from container
}