Interlocked.CompareExchange 使用GREATERTHAN或每种不超过

2019-07-02 11:08发布

所述System.Threading.Interlocked对象允许加法(减法)和比较作为一个原子操作。 看来,一个CompareExchange,只是没有做的平等,但也GREATERTHAN /每种不超过作为一个原子的比较将是非常有价值的。

请问一个假设Interlocked.GreaterThan的IL的特点还是一个CPU级别的功能? 都?

由于缺乏任何其他选项,是有可能创建在C ++这样的特征或直接IL代码和公开该功能为C#?

Answer 1:

更新到后来的文章中,我在这里提出:我们找到了一个更好的办法,使使用额外的锁定对象的更大的比较。 我们为了写了许多单元测试来验证锁和互锁可以一起使用,但仅限于某些情况下。

代码是如何工作的:互锁使用内存壁垒读或写是原子。 需要同步锁,使大于比较一个原子操作。 所以,现在的规则是,这个类中没有其他操作,而这种同步锁定写入值。

我们得到这个类是可以读取速度非常快的联动价值,但写需要多一点点。 阅读是我们的应用程序快约2-4倍。

这里的代码作为视图:

在这里看到: http://files.thekieners.com/blogcontent/2012/ExchangeIfGreaterThan2.png

这里的代码复制和粘贴:

public sealed class InterlockedValue
{
    private long _myValue;
    private readonly object _syncObj = new object();

    public long ReadValue()
    {
        // reading of value (99.9% case in app) will not use lock-object, 
        // since this is too much overhead in our highly multithreaded app.
        return Interlocked.Read(ref _myValue);
    }

    public bool SetValueIfGreaterThan(long value)
    {
        // sync Exchange access to _myValue, since a secure greater-than comparisons is needed
        lock (_syncObj)
        {
            // greather than condition
            if (value > Interlocked.Read(ref  _myValue))
            {
                // now we can set value savely to _myValue.
                Interlocked.Exchange(ref _myValue, value);
                return true;
            }
            return false;
        }
    }
}


Answer 2:

您可以建立其他的原子操作出InterlockedCompareExchange

public static bool InterlockedExchangeIfGreaterThan(ref int location, int comparison, int newValue)
{
    int initialValue;
    do
    {
        initialValue = location;
        if (initialValue >= comparison) return false;
    }
    while (System.Threading.Interlocked.CompareExchange(ref location, newValue, initialValue) != initialValue);
    return true;
}


Answer 3:

你怎么看待这个实现什么:

// this is a Interlocked.ExchangeIfGreaterThan implementation
private static void ExchangeIfGreaterThan(ref long location, long value)
{
    // read
    long current = Interlocked.Read(ref location);
    // compare
    while (current < value)
    {
        // set
        var previous = Interlocked.CompareExchange(ref location, value, current);
        // if another thread has set a greater value, we can break
        // or if previous value is current value, then no other thread has it changed in between
        if (previous == current || previous >= value) // note: most commmon case first
            break;
        // for all other cases, we need another run (read value, compare, set)
        current = Interlocked.Read(ref location);
    }
}


Answer 4:

有了这些辅助方法,你不仅可以交换价值,而且还检测了它更换与否。

用法如下:

int currentMin = 10; // can be changed from other thread at any moment

int potentialNewMin = 8;
if (InterlockedExtension.AssignIfNewValueSmaller(ref currentMin, potentialNewMin))
{
    Console.WriteLine("New minimum: " + potentialNewMin);
}

这里有方法:

public static class InterlockedExtension
{
    public static bool AssignIfNewValueSmaller(ref int target, int newValue)
    {
        int snapshot;
        bool stillLess;
        do
        {
            snapshot = target;
            stillLess = newValue < snapshot;
        } while (stillLess && Interlocked.CompareExchange(ref target, newValue, snapshot) != snapshot);

        return stillLess;
    }

    public static bool AssignIfNewValueBigger(ref int target, int newValue)
    {
        int snapshot;
        bool stillMore;
        do
        {
            snapshot = target;
            stillMore = newValue > snapshot;
        } while (stillMore && Interlocked.CompareExchange(ref target, newValue, snapshot) != snapshot);

        return stillMore;
    }
}


Answer 5:

这并非事实,但认为并发性为2点的形式来它是有用的:

  1. 无锁并发
  2. 基于锁的并发

因为基于软件锁的并发最终使用的堆栈(通常在内核中)的某处锁自由原子指令正在执行这不是真的。 锁自由原子指令,但是,所有最终落得获取存储器总线上的硬件锁。 所以,在现实中,无锁的并发和锁定基于并发是相同的。

但在概念上,在用户应用水平,他们是2种不同的做事方式。

基于锁的并发性是基于“锁定”访问代码的临界段的想法。 当一个线程已“锁定”的一个关键部分,没有其他线程可能有一些代码相同的临界区中运行。 这通常是通过使用“互斥”,而等待进入锁定的临界区成为非运行的,它们与OS调度器和事业的线程来完成。 另一种方法是使用“自旋锁”,它使得一个线程在一个循环中旋转,这样做没有什么用处,直到临界区变为可用。

锁免费并发基于使用原子指令的想法(由CPU专门支持),由硬件保证原子运行。 Interlocked.Increment是无锁并发的一个很好的例子。 它只是调用特殊的CPU指令,做一个单位递增。

锁免费并发是很难的。 它变得特别难的关键部分增加的长度和复杂性。 在临界区的任何步骤可以通过任何数量的线程同时执行一次,并且它们可以在完全不同的速度移动。 你必须确保尽管,系统的整体结果还是正确的。 对于类似的增量,也可以是简单的(在CS只是一个指令)。 对于更复杂的关键部分,事情可能会变得非常复杂非常快。

基于锁的并发还硬,但不太硬如无锁并发。 它允许你创建的代码任意复杂的区域,并且知道只有1线程在任何时候执行它。

锁免费并发但是有一个很大的优势,:速度。 在正确使用时它可以是数量级比基于锁的并发更快。 自旋循环是不好的长期运行的关键部分,因为它们浪费CPU资源无所事事。 因为他们引进了大量的开销互斥可不好的小临界区。 它们包括至少一个模式开关,和多个上下文在最坏的情况下进行切换。

考虑实施托管堆。 调用到OS每次“新”被称为将是可怕的。 它会破坏你的应用程序的性能。 然而,使用无锁并发有可能使用互锁递增(我不知道,如果是这样的CLR做什么来实现第0级的内存分配,但我会感到惊讶,如果事实并非如此。这可以是一个巨大储蓄。

还有其他用途,如无锁数据结构,如持续的堆栈和AVL树。 他们通常使用“CAS”(比较和交换)。

究其原因,然而,该锁定的基于并发和锁定免费并发真的等同是因为每​​个实施细节。

自旋锁通常使用原子指令(通常CAS)在它们的循环条件。 互斥需要使用内部内核结构的任何自旋锁或原子更新在其实施。

原子指令依次使用硬件锁来实现。

在任何情况下,他们都有自己的电视机权衡的,通常在PERF VS复杂性中心。 互斥既可以是比无锁码速度越来越慢。 无锁码既可以是比互斥越来越那么复杂。 使用适当的机制取决于具体情况。

现在,为了回答你的问题:

那做了一个联动的方法比较汇率若小于将意味着给调用者,它不使用锁。 你不能用一个单一的指令实现它以同样的方式增加或比较交换可以做到的。 你可以模仿它做减法(计算小于),在一个循环的互锁比较交流。 你也可以用互斥做到这一点(但是这将意味着一个锁,所以在使用的名称为“联动”会产生误导)。 它是适当的打造“模拟联锁通过CAS”的版本? 那要看。 如果代码被称为非常频繁,并且具有非常小的线程争,那么答案是肯定的。 如果没有,你可以把一个O(1)操作与适度的高持续性因素进入一个无限(或非常长)循环,在这种情况下,它会更好地使用互斥。

大多数时候它是不值得的。



Answer 6:

所有联锁操作已在硬件直接支持。

互锁操作和原子的数据类型是不同的东西..原子类型是库级特性。 在某些平台和某些数据类型原子能公司正在使用互锁指令来实现。 在这种情况下,他们是非常有效的。

在当平台没有在所有的互锁操作,否则将无法适用于某些特定数据类型的其他情况下,库实现使用适当的同步(crit_sect,互斥等),这些操作。

我不知道如果Interlocked.GreaterThan真正需要的。 否则,它可能已经实现。 如果你知道很好的例子,它可能是有用的,我相信每个人在这里会很高兴听到这一点。



Answer 7:

大/小于和等于已经是原子操作。 这并没有解决您的应用程序寿的安全并发行为。

有一个在使它们的联锁家庭的一部分是没有意义的,所以问题是:什么是你真正想达到什么目的?



文章来源: Interlocked.CompareExchange using GreaterThan or LessThan instead of equality