为多个请求提供服务的单个类实例



我的解决方案中有一个单例客户端类,它调用外部服务/API。这个类中没有锁(没有任何东西可以保护多个线程访问的变量(。

如果这个单例实例得到请求1,并且在处理该请求时,它得到另一个请求(请求2(。会发生什么?它是否继续处理请求1直到完成,然后为请求2提供服务?或者它会同时开始为请求2提供服务,而请求2又可能重写这个单例类中的任何变量?谢谢你的帮助!

当两个线程在类的单个实例上同时执行一个方法时,传递给方法的参数和该方法中定义的变量不会被覆盖。但是,字段和属性的值可以更改。

此类是线程安全的:

public class Calculator
{
public long Add(int a, int b)
{
var result = a + b;
return result;
}
}

自变量(ab(和变量(result(存储在堆栈帧中,堆栈帧是每次执行该方法时分配的内存。因此,对于每个方法调用都存在result变量。两个方法调用不能共享或覆盖该变量。

类似地,如果此方法在执行时被调用,则ab参数不会被覆盖。

因此,任何数量的线程都可以在Calculator的单个实例上安全地调用Add方法。他们可以同时这样做。一个执行不等待另一个执行。

这个类是线程安全的:

public class Calculator
{
private int _a;
private int _b;
public long Add(int a, int b)
{
_a = a;
_b = b;
var result = _a + _b;
return result;
}
}

这是一个人为的例子。这里的区别在于调用Add会修改类的状态,从而更改_a_b字段。如果这些是属性而不是字段,情况也是一样的。

如果两个线程同时尝试执行此操作,将会得到不可预测的结果。就在第一个线程添加_a_b之前,另一个线程可能会更改其中一个字段的值。


这是一个简单的版本。还有更复杂的情况。例如,假设我们将一个List<int>作为参数传递给一个方法。如果另一个线程引用了同一个列表,那么两个线程都可以尝试修改它,或者一个线程可以在另一个正在读取它的时候修改它,所有这些都会产生意外的结果。如何管理这一切超出了这个答案的范围。

以下是一些要点:

  • 除非需要,否则不要将状态添加到类(调用构造函数后更改的字段和属性(。有些情况下,我们必须这样做,在某些情况下,这就是课程的全部目的。但是,如果你可以在上面的例子中进行选择,一定要选择第一个
  • 每当我们传递对对象的引用时,比如我们自己类的列表或实例,请考虑如果两个线程可以访问该对象会发生什么。然后
    • 如果它们不是线程安全的,就不要到处传递
    • 在它们被传来传去之前确保它们的线程安全
    • 要非常小心。这是最糟糕的选择,因为它给我们和未来的开发人员带来了负担,让他们在脑海中模拟代码的行为,并发现潜在的问题。与其想办法解决问题,不如预防问题

另一种描述方式:并发性就像钚。它强大而有用,但我们必须始终知道它在哪里,并确保它永远不会泄漏。

最新更新