2016-07-26 47 views
1

我正在考虑某种类型的同步原语,但我不知道调用了这种类型的同步,或者如果这样的事情会起作用。没有等待的并发代码

所以有一个变量(布尔),它基本上表示一个线程是否仍在一块内存上工作。在开始时,bool被设置为false,这意味着工作线程不在你的内存块上工作。现在主线程给工作线程一个“todo-list”,描述它应该如何处理这块内存。之后,它将布尔状态更改为true,以便工作线程知道现在可以完成其工作。主线程现在可以继续其自己的工作,并在某些位置检查工作线程是否已经完成工作,例如,如果布尔值已被设置为false。如果是stil true,主线程只是继续自己的工作,并不等待工作线程。如果布尔值为false,则主线程知道工作线程已完成并开始处理内存块。

所以布尔值只是将所有权转移给两个线程之间的内存块。如果一个线程当前没有该内存的所有权,那么它就继续自己的工作,并且如果它现在再次拥有所有权,则重复检查。这样,没有一个线程正在等待另一个线程并且可以继续自己的工作。

这是什么叫,以及如何实现这样的行为?

编辑:基本上它是一个互斥体。但与其等待互斥锁再次解锁,它将继续/跳过关键代码。

+0

这可能是'std :: future'和/或'std :: packaged_task'的好例子。 – amuttsch

+0

定期检查某些外部事件时做自己的工作称为“轮询”。它可以用一个原子变量来实现。这通常不是一个好主意。 –

+0

你能解释一下为什么吗? –

回答

1

编辑:基本上它是一个互斥体。但不是等待互斥锁再次解锁,它会继续/跳过关键代码。

它仍然是一个互斥体,只是用“尝试”方法。

标准C++

,我们谈论std::mutex::try_lock,它试图锁定互斥体,如果失败则返回false和

class unlocker{ 
std::mutex& m_Parent; 

public : 

unlocker(std::mutex& parent) : m_Parent(parent){} 
~unlocker() {m_Parent.unlock(); } 

}; 

std::mutex mtx; 
if (mtx.try_lock()){ 
    unlocker unlock(mtx); // no, you can't use std::lock_guard/unique_lock here 
    //success, mtx is free 
} else{ 
    // do something else 
} 

在本地操作系统的代码移到你有这取决于操作类似的功能系统,例如Unix上的pthread_mutex_trylock和Windows上的TryEnterCriticalSection。不用说标准互斥量可能在幕后使用这些函数

+0

这是一个有效的方法?我需要问 - 在并发编程中,似乎很简单的应用大多数时候都是错误的。 –

+0

如果是正确的做法=取决于应用程序。通常,更好的选择是首先减少一般的争用或切换到无锁数据类型。但无论如何,你似乎对性能感到困扰。我认为应该首先证明争用是他的瓶颈 –

+0

'try_lock'的问题是它不可靠。 Mutex可以很好地保护并发访问资源,但它在信号传输方面做得相当糟糕。在C++线程库中,如果您需要信号传输,您可能需要将来(用于一次性信号)或condition_variable(用于可重用信号)。 – ComicSansMS

0

如果主线程不工作,你会怎么做?

假设你继续检查,你会继续阅读true。最终你会达到主线程无法继续工作线程结果的地步。由于您没有更多的工作要做,现在唯一剩下的就是不断检查标志的值,浪费其他线程可用于执行有用工作的CPU资源。

一般来说,这不是你想要的。相反,您希望操作系统将主线程置于睡眠状态,并且只有在工作线程完成处理时才将其唤醒。随现代操作系统一起提供的各种锁和信号量以这种方式工作。在内存下面有一些标志表明谁拥有锁,但是也有一些逻辑周围的逻辑确保操作系统不会安排无关的线程,而是等待锁定准备就绪。

这就是说,有些情况下,这不是你想要的。如果您足够确信您不会遇到一个线程旋转锁的情况,并且您希望节省操作系统锁定带来的开销,那么只需检查像您所描述的标志可能是可行的选项。

请注意,虽然像这样的低级别的东西应该保留给特殊情况,而不是您的工具箱中的第一个工具。这很容易导致算法错误,或者执行效率不如您想象的那么高。如果你决定走这条路,准备好做一些认真的工作,让它按预期工作。