C#和#x2B+;操作员在foreach循环中成为线程安全的?

C#和#x2B+;操作员在foreach循环中成为线程安全的?,c#,foreach,thread-safety,increment,interlocked-increment,C#,Foreach,Thread Safety,Increment,Interlocked Increment,最近我从VB转到了C#,所以我经常使用C#到VB.NET转换器来理解语法差异。 在将下一个方法移到VB时,我注意到一件有趣的事情 原代码: public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools) { int trueCnt = 0; foreach(bool b in bools) if (b && (++trueCnt > threshold))

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

原代码:

 public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
{
   int trueCnt = 0;
   foreach(bool b in bools)
      if (b && (++trueCnt > threshold)) 
          return true;
   return false;          
} 
C#的
++
运算符替换为
系统.线程.联锁.增量

这是否意味着如果在
foreach
循环中使用非线程安全的
++
运算符,它将成为线程安全的?它是一种语法糖吗?如果这是真的,那么为什么转换器在VB版本中放置
联锁.Increment
?我认为foreach在C#和VB中的工作原理完全相同。或者它只是一个转换器“保险”?

我相信这是因为你想在做比较的同一语句中增加值。我没有太多的理由来证明这一点,但它确实感觉像是正确的答案,因为trueCnt+=1不允许在同一行中进行比较。它当然与foreach无关,尝试在循环外添加相同的行,我几乎可以肯定它也会转换为Increment。在VB.Net中,没有其他语法可以在同一行中同时进行增量和比较。

我确信这只是一个转换器破解,我想我可以解释这背后的原因

但是首先,为了回答您的问题,C#中内置的
++
操作符不是线程安全的。它只是以下过程的一个语法糖(在
++i
的情况下):

  • 读取
    i的值
  • 增加它
  • 将其写回
    i
  • 返回递增的值
因为有一个单独的读写操作,所以这是一个非原子操作

现在,在VB中没有直接等价的
++
操作符。最接近的是:

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

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

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

if(b&&(++trueCnt>阈值))
...
如果b那么
trueCnt+=1
如果trueCnt>阈值,则
...
如果结束
如果结束
正如你所见,这需要更多的重写。如果
trueCnt
是一个属性,甚至需要引入一个单独的临时变量(以避免对其进行两次计算)


这需要比转换器使用的更简单的语法转换更深入的语义分析和控制流重写,因为在VB的表达式中不能使用
trueCnt+=1

除了上述问题之外,C的默认行为遵循Java不幸的整数溢出行为。虽然有时包装整数溢出语义是有用的,但C#通常不区分整数应该在溢出时包装的情况和整数不应该包装的情况,但程序员认为溢出捕获是不值得的。这可能会混淆转换工作,因为VB.NET使捕获行为比包装行为更容易、更快,而C#则相反。因此,出于速度原因,翻译使用未检查数学的代码的逻辑方法是在VB.NET中使用普通检查数学,而翻译需要包装行为的代码的逻辑方法是在VB.NET中使用包装整数方法。
Threading.interlocated.Increment
Threading.Increment.Add
方法使用包装整数行为,因此虽然从速度角度看它们不是最优的,但它们很方便。

不知道是否有任何开销,例如++与interlock。@Steve
interlock.Increment
稍微慢一点,因为它引入了内存隔离。但是,除非您处理的是非常高性能的代码,否则差异不会明显,在这种情况下,您可能不会自动转换代码。
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