2015-04-04 21 views
11

Recentemente ho spostato da VB a C#, quindi uso spesso un convertitore C# in VB.NET per capire le differenze di sintassi. Spostando il prossimo metodo su VB ho notato una cosa interessante.L'operatore C# ++ diventa thread-safe nel ciclo foreach?

C# codice originale: risultato

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:

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 ++ operatore sostituita con System.Threading.Interlocked.Increment Vuol dire che non threadsafe ++ operatore diventare threadsafe se utilizzato in foreach ciclo? È una specie di zucchero sintassi? Se questo è vero, allora perché il convertitore ha posizionato Interlocked.Increment nella versione VB? Ho pensato che foreach sia in C che in VB funziona esattamente allo stesso modo. O è solo un convertitore "assicurazione"?

risposta

11

Sono sicuro che si tratta semplicemente di una modifica del convertitore e penso di poter spiegare il ragionamento alla base di questo.

Ma prima, solo per rispondere alla tua domanda, l'operatore ++ integrato in C# non è thread-safe. È semplicemente uno zucchero sintattico per il seguente processo (nel caso di ++i):

  • leggere il valore di i
  • incrementarlo
  • riscriverlo i
  • ritorno il valore incrementato

Dato che c'è una lettura e una scrittura separate, questa è un'operazione non atomica.

Ora, in VB non esiste un equivalente diretto dell'operatore ++. La cosa più vicina è:

i += 1 

ma questo è un dichiarazione. Al contrario, ++i è un'espressione . È possibile utilizzare ++i all'interno di un'altra istruzione o espressione, ma non è possibile farlo con le istruzioni VB.

Utilizzare Interlocked.Increment è solo un modo intelligente per tradurre facilmente il codice, senza dover suddividere l'intera istruzione in più altre istruzioni.

Senza questo trucco, il convertitore dovrebbe suddividere l'espressione in questo modo:

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

Il che, come si può vedere, richiede molto più riscrittura. Richiederebbe anche l'introduzione di una variabile temporanea separata se trueCnt fosse una proprietà (per evitare di valutarla due volte).

Ciò richiede una più profonda semantica analisi e controllo di flusso riscrittura che la conversione sintattica più semplice il vostro convertitore utilizzato - solo perché trueCnt += 1 non possono essere utilizzati all'interno un'espressione in VB.

+0

Chiedo se c'è un sovraccarico, ad esempio ++ vs interlock. –

+0

@Steve 'Interlock.Increment' è un po 'più lento, perché introduce una fence di memoria. Ma la differenza non sarà evidente a meno che non si abbia a che fare con codice ad alte prestazioni, nel qual caso probabilmente non lo si auto-convertirà comunque. –

4

Credo che sia perché si desidera incrementare il valore nella stessa istruzione del confronto. Non ho troppe giustificazioni per questo, ma sicuramente mi sembra la risposta giusta come trueCnt + = 1 non consente un confronto nella stessa riga. Certamente non ha nulla a che fare con il fatto di essere un foreach, provare ad aggiungere la stessa linea al di fuori del loop e sono quasi certo che convertirà anche in Increment. Semplicemente non c'è altra sintassi in VB .Net per incrementare e confrontare nella stessa riga.

2

Oltre ai suddetti problemi, il comportamento predefinito di C# segue lo sfortunato comportamento di overflow di Java. Sebbene ci siano momenti in cui la semantica di overflow-overflow è utile, C# generalmente non fa alcuna distinzione tra situazioni in cui gli interi dovrebbero avvolgere l'overflow rispetto a quelli in cui non ci si aspetta che gli interi avvolgano, ma il programmatore non pensa che il trapping sia utile. Questo può confondere gli sforzi di conversione perché VB.NET rende il comportamento di trapping più facile e più veloce del comportamento di wrapping, mentre C# fa il contrario. Di conseguenza, il modo logico per tradurre il codice che utilizza la matematica non controllata per ragioni di velocità sarebbe utilizzare la matematica ordinaria controllata in VB.NET, mentre il modo logico di tradurre il codice che richiede il wrapping sarebbe utilizzare i metodi di wrapping integer in VB.NET . I metodi Threading.Interlocked.Increment e Threading.Increment.Add usano il wrapping del comportamento intero, quindi, mentre non sono ottimali dal punto di vista della velocità, sono convenienti.