Search code examples
c#foreachthread-safetyincrementinterlocked-increment

Does C# ++ operator become threadsafe in foreach loop?


Recently I moved from VB to C#, so I often use a C# to VB.NET converter to understand syntax differences. While moving next method to VB I noticed an interesting thing.

C# original code:

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

VB.NET result:

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

C#'s ++ operator replaced with System.Threading.Interlocked.Increment Does it mean that not threadsafe ++ operator become threadsafe if used in foreach loop? Is it a kind of syntax sugar? If that is true, then why converter placed Interlocked.Increment in VB version? I thought foreach in both C# and VB works exactly the same. Or it's just a converter "insurance"?


Solution

  • I'm sure it's simply a converter hack, and I think I can explain the reasoning behind this.

    But first, just to answer your question, the built-in ++ operator in C# is not thread-safe. It's simply a syntactic sugar for the following process (in the case of ++i):

    • read the value of i
    • increment it
    • write it back to i
    • return the incremented value

    As there's a separate read and write, this is a non-atomic operation.

    Now, in VB there is no direct equivalent of the ++ operator. The closest thing is:

    i += 1
    

    but this is a statement. By contrast, ++i is an expression. You can use ++i inside another statement or expression, but you can't do so with VB statements.

    Using Interlocked.Increment is only a clever way to translate the code easily, without having to break down the whole statement into multiple other statements.

    Without this trick, the converter would have to break down the expression like this:

    if (b && (++trueCnt > threshold))
        ...
    
    If b Then
        trueCnt += 1
        If trueCnt > threshold Then
            ...
        End If
    End If
    

    Which, as you can see, requires much more rewriting. It would even require introducing a separate temporary variable if trueCnt were a property (to avoid evaluating it twice).

    This requires a deeper semantic analysis and control flow rewriting than the simpler syntactic conversion your converter used - just because trueCnt += 1 can't be used inside an expression in VB.