在#ach循环中,C#++运算符是否成为线程安全的?

AsV*_*leO 11 c# foreach increment thread-safety interlocked-increment

最近我从VB转到C#,所以我经常使用C#到VB.NET转换器来理解语法差异.在将下一个方法移动到VB时,我发现了一个有趣的事情

C#原始代码:

 public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
{
   int trueCnt = 0;
   foreach(bool b in bools)
      if (b && (++trueCnt > threshold)) 
          return true;
   return false;          
} 
Run Code Online (Sandbox Code Playgroud)

VB.NET结果:

Public Function ExceedsThreshold(threshold As Integer, bools As IEnumerable(Of Boolean)) As Boolean
Dim trueCnt As Integer = 0
For Each b As Boolean In bools
    If b AndAlso (System.Threading.Interlocked.Increment(trueCnt) > threshold) Then
        Return True
    End If
Next
Return False End Function
Run Code Online (Sandbox Code Playgroud)

C#的++运算符被替换为System.Threading.Interlocked.Increment 是否意味着++如果在foreach循环中使用,线程安全运算符不会成为线程安全的?它是一种语法糖吗?如果这是真的,那么为什么转换器放在Interlocked.IncrementVB版本中?我认为C#和VB中的foreach完全相同.或者它只是一个转换器"保险"?

Luc*_*ski 11

我确定它只是一个转换器黑客,我想我可以解释这背后的原因.

但首先,只是回答你的问题,++C#中的内置运算符不是线程安全的.它只是以下过程的语法糖(在这种情况下++i):

  • 读取的值 i
  • 增加它
  • 把它写回去 i
  • 返回递增的值

由于存在单独的读写,因此这是非原子操作.

现在,在VB中没有直接等效的++运算符.最接近的是:

i += 1
Run Code Online (Sandbox Code Playgroud)

但这是一个声明.相比之下,++i是一种表达方式.您可以++i在另一个语句或表达式中使用,但不能使用VB语句.

使用Interlocked.Increment只是一种轻松翻译代码的聪明方法,而不必将整个语句分解为多个其他语句.

如果没有这个技巧,转换器必须像这样分解表达式:

if (b && (++trueCnt > threshold))
    ...
Run Code Online (Sandbox Code Playgroud)
If b Then
    trueCnt += 1
    If trueCnt > threshold Then
        ...
    End If
End If
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这需要更多的重写.如果trueCnt是属性,它甚至需要引入一个单独的临时变量(以避免对它进行两次评估).

这需要更深层次的语义分析和控制流重写比简单句法转换器的使用-只是因为trueCnt += 1不能使用内部在VB中的表达.