多线程无锁

仅仅因为您在线程之间共享数据结构并不意味着您必须使用锁。

多年来,我遇到了一些方法,这些方法可以帮助协调线程之间的工作,而不必等待任何人等待。 这里有几个。

警告:我停止使用这些模式是因为它们容易被误解和误用,这可能会导致难以诊断的随机错误。 如果使用这些模式,则不会帮助下一个开发人员更好地理解代码或避免错误。 你被警告了。

Reference to an immutable object

我们知道在多线程环境中,修改不带锁的共享对象是一个坏主意。 但是替换它呢?

例如,考虑此代码。 我将使用C#,但是在Java,C ++或其他具有线程的传统语言中也是如此。

public class FunWithThreads {
    private Thing thing;

    public void InThreadOne() {
        this.thing = new Thing()
    }

    public Result InThreadTwo() {
        var localThing = this.thing;
        var result1 = SomeCalculation1(localThing);
        var result2 = SomeCalculation2(localThing);
        var finalResult = DoMoreWork(localThing, result1, result2);
        return result;
    }
}

假设有许多线程正在同时调用InThreadOne(),而许多其他线程正在同时调用InThreadTwo()。 我们会得到错误的结果,比赛条件或冲突吗?

并不是的。InThreadTwo()始终指的是一致版本这东西因为它将对它的引用放置到局部变量中,并且从那时起仅使用局部引用。 如果计算花费的时间太长,那么也许我们得到的结果有些陈旧,但结果始终是一致的。

有用的地方:我发现这种方法在任何时候都非常有用这东西是一个大型的,预先计算的数据结构,不经常更新但经常读取。 通常,我已经使用它为内部资本市场应用程序缓存了一些数据,以使该应用程序对用户而言更加敏捷。

陷阱:确保这一点非常重要这东西在方法开始时仅被引用一次,否则我们将回到比赛状态。 同样重要的是这东西分配后永远不会修改。 这意味着很多描述性注释,非常仔细的代码审查,并确保每个看过此代码的开发人员都知道所有问题。 在更大的代码库中,某人(甚至您也可以!)可能仍然找到在某处犯错误的方法。

Complex atomic operations

上面的代码起作用的原因是因为在C#(和大多数其他语言)中,引用的分配是原子操作,即在执行时,另一个线程无法中途潜入并做其他事情。

Modern processors have a few other interesting atomic operations, like compare and swap, implemented in C# as Interlocked.CompareExchange(), and in Java as AtomicInteger.compareAndSet(). This function takes three arguments and does the following as an atomic operation:

  1. 比较第一个和第三个参数,然后如果它们相等,则将第一个参数设置为等于第二个参数,或者如果它们不相等,则不执行任何操作。

可以使用这种情况的一种情况是尝试限制对有限资源上昂贵操作的请求。 例如,如果昂贵的操作已在运行,则此代码将删除所有请求:

// Let's pretend this class is a singleton.
public class ExpensiveOperationLimiter {
    private int isProcessing = 0; 

    public void RequestExpensiveOperation() {
        // If isProcessing == 1, this does nothing and returns 1.
        // If isProcessing == 0, this sets isProcessing to 1 and returns 0. 
        var wasProcessing = Interlocked.CompareExchange(ref isProcessing, 1, 0);

        if (!wasProcessing) {
            // Only one invocation will be running at a time.
            DoExpensiveOperation()
        }

        isProcessing = 0;
    }

    private void DoExpensiveOperation() {
        // ...
    }
}

如果仔细看一下上面的代码,您将发现您无法替换Interlocked.CompareExchange()并且仍然具有线程安全性-在指令之间总是会有另一个线程潜入的机会。

经过一些工作,该代码也可以扩展为排队下一个请求。

有用的地方注意:在事件可以随机触发应用程序服务器的缓存从数据库或另一个系统刷新的情况下,我使用了这种代码。

陷阱:通常,我认为这是微优化,不值得付出努力。 许多开发人员了解或听说过锁,并且比起任何涉及比较设置的代码,使用锁的时间更短。 与以前一样,使用此方法可能会冒犯某个人(包括您!)的风险,最终会犯错。

The best way to avoid locks

不要编写多线程代码!

A shameless plug

Check out Maesure, a time tracker that asks you every 10 minutes what you're doing.

from: https://dev.to//iourikhramtsov/multi-threading-without-locks-fgb

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值