C# vs Java 线程安全



在Java中,我们可以做类似的事情

final string str = "I am a string";
Thread thread = new Thread(...{
string str2 = "bla bla bla " + str;
});

我不完全明白该怎么做,但关键是我需要将 str 声明为 final,以便在我的线程中访问它,这样就不会有可变的损坏。

但是现在我该如何在 C# 中做到这一点

string str = "I am a string";
Task.Run(() => {
string str2 = "bla bla " + str;
});

它确实有效,但我不确定这是正确的方法。我正在使用Visual Studio for Xamarin。

让我们退后一步。

在 C# 中创建 lambda 时,将外部变量捕获为变量

int x = 123;
Action change = () => { Console.WriteLine(x); x = 345; };
Console.WriteLine(x); // 123
change(); // 123
Console.WriteLine(x); // 345
change(); // 345
x = 789;
change(); // 789
Console.WriteLine(x); // 345

只有一个变量 x,它由 lambda 和方法的激活共享

这非常有用,但在某些情况下可能会变得非常混乱

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x)
{
actions.Add(() => { Console.WriteLine(x); });
}
actions[0](); // 10, surprise!

只有一个变量 x,所有 lambda 都共享它。

有些人想要第一种行为——lambda共享变量——但编写第二种程序的人想要第二种行为。 他们希望 lambda 捕获变量的当前值,而不是变量本身

C# 只需选择第一个选项即可解决此问题。 Java通过使第一种场景非法化来解决这个问题。 如果您只能捕获永不更改的变量,那么您既没有将变量捕获为变量的好处,也没有问题。

我个人更喜欢 C# 选择,尽管它有缺点。但两者都是合理的、合理的和有原则的。不同的语言设计师会做出不同的选择。

该功能本身与线程安全无关。 (尽管它有线程安全方面;在 C# 中通过 lambda 在线程之间共享的变量不是易失性的。相反,这是一个关于变量的 lambda 捕获如何工作的设计决策。

我不

完全明白该怎么做,但关键是我需要将 str 声明为 final才能在我的线程中访问它,所以没有 可变的腐败。

不,关键是线程声明包含一个Java闭包,这就是Java闭包的工作方式,即它们只能使用不可变(final)对象创建。C# 中不存在此问题。

C# 中final的等效项是关键字readonly如果它像您正在做的那样用于字段,或者sealed是否用于类或方法。

对于要在另一个线程中使用的变量,使用readonly并不是严格要求的,您的代码现在将按原样工作,但它确实可以确保您不会意外地犯错误,这些错误可能导致不一致的行为,因为您更改了其他线程上的str值。

最新更新