eine andere Art und Weise - dies den Vorteil, dass Sie nachweisen können, dass das Schloss genommen wurde:
void bar_impl(std::unique_lock<std::mutex> lock)
{
assert(lock.owns_lock());
// real work
}
void bar()
{
bar_impl(std::unique_lock<std::mutex>(mx_));
}
void foo()
{
// stuff
bar_impl(std::unique_lock<std::mutex>(mx_));
// more stuff
}
Begründung:
std::mutex
ist beweglich nicht (von der Norm seinem Mandat), aber ein std::unique_lock<std::mutex>
ist. Aus diesem Grund können wir eine Sperre in einen Angerufenen verschieben und bei Bedarf an einen Anrufer zurücksenden.
Dies ermöglicht es uns, das Eigentum an der Sperre in jeder Phase einer Aufrufkette nachzuweisen.
Sobald der Optimierer involviert ist, ist es wahrscheinlich, dass alle Lock-Moving-Vorgänge entfernt werden. Dies gibt uns das Beste aus beiden Welten - beweisbares Eigentum und maximale Leistung.
Ein vollständigeres Beispiel:
#include <mutex>
#include <cassert>
#include <functional>
struct actor
{
//
// public interface
//
// perform a simple synchronous action
void simple_action()
{
impl_simple_action(take_lock());
}
/// perform an action either now or asynchronously in the future
/// hander() is called when the action is complete
/// handler is a latch - i.e. it will be called exactly once
/// @pre an existing handler must not be pending
void complex_action(std::function<void()> handler)
{
impl_complex_action(take_lock(), std::move(handler));
}
private:
//
// private external interface (for callbacks)
//
void my_callback()
{
auto lock = take_lock();
assert(!_condition_met);
_condition_met = true;
impl_condition_met(std::move(lock));
}
// private interface
using mutex_type = std::mutex;
using lock_type = std::unique_lock<mutex_type>;
void impl_simple_action(const lock_type& lock)
{
// assert preconditions
assert(lock.owns_lock());
// actions here
}
void impl_complex_action(lock_type my_lock, std::function<void()> handler)
{
_handler = std::move(handler);
if (_condition_met)
{
return impl_condition_met(std::move(my_lock));
}
else {
// initiate some action that will result in my_callback() being called
// some time later
}
}
void impl_condition_met(lock_type lock)
{
assert(lock.owns_lock());
assert(_condition_met);
if(_handler)
{
_condition_met = false;
auto copy = std::move(_handler);
// unlock here because the callback may call back into our public interface
lock.unlock();
copy();
}
}
auto take_lock() const -> lock_type
{
return lock_type(_mutex);
}
mutable mutex_type _mutex;
std::function<void()> _handler = {};
bool _condition_met = false;
};
void act(actor& a)
{
a.complex_action([&a]{
// other stuff...
// note: calling another public interface function of a
// during a handler initiated by a
// the unlock() in impl_condition_met() makes this safe.
a.simple_action();
});
}
Unter Verwendung eines 'std :: mutex' üerhaps? –
Übergeben Sie den Mutex als Parameter an den refaktorierten Abschnitt des gemeinsam genutzten Codes. –
@ πάνταῥεῖ: "ähmaps" klingt wie eine Krankheit, die du aus dem Urlaub mit nach Hause bringst, oder vielleicht von einem Metalkonzert. –