交换互斥锁

时间:2018-07-15 21:37:03

标签: c++ c++11 c++14 c++17

我无法正确使用“交换”锁。考虑这种情况:

bool HidDevice::wait(const std::function<bool(const Info&)>& predicate)
{
    /* A method scoped lock. */
    std::unique_lock waitLock(this->waitMutex, std::defer_lock);

    /* A scoped, general access, lock. */
    {
        std::lock_guard lock(this->mutex);

        bool exitEarly = false;

        /* do some checks... */

        if (exitEarly)
            return false;

        /* Only one thread at a time can execute this method, however
        other threads can execute other methods or abort this one. Thus,
        general access mutex "this->mutex" should be unlocked (to allow threads
        to call other methods) while at the same time, "this->waitMutex" should
        be locked to prevent multiple executions of code below. */

        waitLock.lock(); // How do I release "this->mutex" here?
    }

    /* do some stuff... */

    /* The main problem is with this event based OS function. It can 
    only be called once with the data I provide, therefore I need to 
    have a 2 locks - one blocks multiple method calls (the usual stuff) 
    and "waitLock" makes sure that only one instance of "osBlockingFunction" 
    is ruinning at the time. Since this is a thread blocking function,
    "this->mutex" must be unlocked at this point. */

    bool result = osBlockingFunction(...);

    /* In methods, such as "close", "this->waitMutex" and others are then used 
    to make sure that thread blocking methods have returned and I can safely
    modify related data. */

    /* do some more stuff... */

    return result;
}

如何在不使代码过于复杂的情况下解决此“交换”问题?我可以在锁定另一个this->mutex之前将其解锁,但是,恐怕在那一纳秒内,可能会发生竞争状况。

编辑:

想象一下,有3个线程正在调用wait方法。第一个将锁定this->mutex,然后锁定this->waitMutex,然后解锁this->mutex。第二个将锁定this->mutex,并且必须等待this->waitMutex可用。它不会解锁this->mutex。第三个将卡在锁定this->mutex上。

我想让最后2个线程等待this->waitMutex可用。

编辑2:

使用osBlockingFunction的示例。

2 个答案:

答案 0 :(得分:3)

闻起来,设计/实现应该与enter code hereclass CenterView : View("My View") { private val ExcelHandler: ExcelController by inject() private var content = mutableListOf<Products>().observable() private var rowcounter = SimpleIntegerProperty() private var oktorun = SimpleBooleanProperty(true) val table = TableView<Products>() val data = SortedFilteredList(content).bindTo(table) private var Accounts = mutableListOf<String>().observable() override val root = borderpane() { bottom{ hbox(10) { style{ padding = box(10.px) alignment = Pos.CENTER } button("Import"){ addClass(Styles.btmDesign) style{ backgroundColor += Color.PURPLE fontFamily = "Comic Sans MS" } useMaxWidth = true enableWhen(oktorun) action { oktorun.value = false runAsync { ExcelHandler.readExcel("data/test.xlsx") } ui { loadedText -> content.isEmpty() content.addAll(loadedText.observable()) rowcounter.value = loadedText.count() Accounts.addAll(loadedText.map { it.Account }.toList().distinct() .observable()) oktorun.value = true } } } hbox{ label("Rows ") { style{ fontSize = 20.px fontWeight = FontWeight.BOLD } } label { style{ fontSize = 20.px fontWeight = FontWeight.BOLD textFill = Color.GREEN } bind(rowcounter) } } textfield { promptText = "Filter" data.filterWhen(textProperty()) { query, item -> item.Account!!.contains(query, ignoreCase = true) } } } left{ listview(Accounts) { style{ padding = box(20.px) fontSize = 20.px fontWeight = FontWeight.BOLD } } } center{ style{ prefWidth = 100.px alignment = Pos.CENTER padding = box(20.px) } tableview(data) { isEditable = true style{ fontSize =20.px fontWeight = FontWeight.EXTRA_BOLD } column("External", Products::ExternalProperty) column("ItemRelation", Products::ItemRelationProperty) column("Account",Products::AccountProperty) column("Price", Products::AmmountProperty) column("currency", Products::ValutaProperty) column("Date", Products::DateProperty) column("Status", Products::StatusProperty) enableDirtyTracking() columnResizePolicy = SmartResize.POLICY }.multiSelect(true) } right { } } } } 上的std::condition_variable cv有点不同,并且只有一个互斥体。当您编写“其他线程可以执行其他方法或中止该方法”时,将调用HidDevice::wait来“中止”该等待。 cv.notify_one {自动进入等待并解锁互斥锁},在cv.wait {自动进入等待并锁定互斥锁}。像cv.notify这样更简单:

HidDevice::wait
  • 我的假设是(根据函数的名称)在bool HidDevice::wait(const std::function<bool(const Info&)>& predicate) { std::unique_lock<std::mutex> lock(this->m_Mutex); // Only one mutex. m_bEarlyExit = false; this->cv.wait(lock, spurious wake-up check); if (m_bEarlyExit) // A bool data-member for abort. return; /* do some stuff... */ } 上,线程等待直到某些逻辑成真。

“中止”等待,将由另一个线程调用的其他/* do some checks... */函数负责:

HidDevice

类似的东西。

答案 1 :(得分:0)

我建议创建一个小的“解锁”工具。这是具有反向语义的互斥包装。在lockunlocks,反之亦然:

template <class Lock>
class unlocker
{
    Lock& locked_;

public:
    unlocker(Lock& lk) : locked_{lk} {}

    void lock() {locked_.unlock();}
    bool try_lock() {locked_.unlock(); return true;}
    void unlock() {locked_.lock();}
};

现在代替:

waitLock.lock(); // How do I release "this->mutex" here?

您可以说:

unlocker temp{lock};
std::lock(waitLock, temp);

其中lockunique_lock,而不是lock_guard持有mutex

这将锁定waitLock并解锁mutex,就像通过一条不间断的指令一样。


现在,在对所有这些进行编码之后,我可以推断它可以转换为:

waitLock.lock();
lock.unlock();  // lock must be a unique_lock to do this

第一个版本是否或多或少可读性是一个观点问题。第一个版本更容易推论(一旦知道std::lock会做什么)。但是第二个更简单。但是在第二篇中,读者必须更仔细地考虑正确性。


更新

只需阅读问题中的编辑内容即可。此解决方案无法解决编辑中的问题:第二个线程将阻止第三个(及后续线程)在需要mutex但不需要waitMutex的任何代码中取得进展,直到第一个线程释放{ {1}}。

因此,从这个意义上讲,我的回答在技术上是正确的,但不满足所需的性能特征。我将其留作参考。