Operações interligadas
O Interlocked classe fornece métodos que sincronizar o acesso a uma variável que é compartilhada por vários threads. Os segmentos de processos diferentes podem usar esse mecanismo se a variável na memória compartilhada. Operações interligadas são atômicas — ou seja, toda a operação é uma unidade que não pode ser interrompida por outra operação interligada na mesma variável. Isso é importante em sistemas operacionais com preemptiva multithreading, onde um segmento pode ser suspenso após o carregamento de um valor de um endereço de memória, mas antes de ter a chance de alterá-lo e armazená-lo.
O Interlocked classe fornece as seguintes operações:
No.NET Framework versão 2.0, o Add método adiciona um valor inteiro a uma variável e retorna o novo valor da variável.
No.NET Framework versão 2.0, o Read método lê um valor inteiro de 64 bits, como uma operação atômica. Isso é útil em sistemas operacionais de 32 bits, onde a leitura de um inteiro de 64 bits não é normalmente uma operação atômica.
O Increment e Decrement métodos incrementam ou decrementam a uma variável e retornam o valor resultante.
O Exchange método realiza uma troca atômica do valor na variável especificada, retornando o valor e substituí-lo com um novo valor. No.NET Framework versão 2.0, uma genérica sobrecarga desse método pode ser usado para realizar essa troca em uma variável de qualquer tipo de referência. Consulte Exchange<T>(T, T).
O CompareExchange método também troca dois valores, mas contingente no resultado da comparação. No.NET Framework versão 2.0, uma genérica sobrecarga desse método pode ser usado para realizar essa troca em uma variável de qualquer tipo de referência. Consulte CompareExchange<T>(T, T, T).
Nos processadores modernos, os métodos da Interlocked classe geralmente pode ser implementado por uma única instrução. Assim, eles fornecem a sincronização de alto desempenho e pode ser usados para criar mecanismos de sincronização de nível mais alto, como bloqueios de rotação.
Para obter um exemplo que usa o Monitor e Interlocked classes em combinação, consulte Monitores.
Exemplo de CompareExchange
O CompareExchange método pode ser usado para proteger os cálculos que são mais complicados do que simples increment e decrement. O exemplo a seguir demonstra um método de thread-safe que adiciona um total contínuo armazenado como um flutuante número de ponto. (Para inteiros, o Add método é uma solução mais simples.) Para obter exemplos de código completo, consulte os métodos sobrecarregados de CompareExchange que levar argumentos de ponto flutuante de precisão simples e de precisão dupla (CompareExchange(Single, Single, Single) e CompareExchange(Double, Double, Double)).
Imports System
Imports System.Threading
Public Class ThreadSafe
' totalValue contains a running total that can be updated
' by multiple threads. It must be protected from unsynchronized
' access.
Private totalValue As Double = 0.0
' The Total property returns the running total.
Public ReadOnly Property Total As Double
Get
Return totalValue
End Get
End Property
' AddToTotal safely adds a value to the running total.
Public Function AddToTotal(addend As Double) As Double
Dim initialValue, computedValue As Double
Do
' Save the current running total in a local variable.
initialValue = totalValue
' Add the new value to the running total.
computedValue = initialValue + addend
' CompareExchange compares totalValue to initialValue. If
' they are not equal, then another thread has updated the
' running total since this loop started. CompareExchange
' does not update totalValue. CompareExchange returns the
' contents of totalValue, which do not equal initialValue,
' so the loop executes again.
Loop While initialValue <> Interlocked.CompareExchange( _
totalValue, computedValue, initialValue)
' If no other thread updated the running total, then
' totalValue and initialValue are equal when CompareExchange
' compares them, and computedValue is stored in totalValue.
' CompareExchange returns the value that was in totalValue
' before the update, which is equal to initialValue, so the
' loop ends.
' The function returns computedValue, not totalValue, because
' totalValue could be changed by another thread between
' the time the loop ends and the function returns.
Return computedValue
End Function
End Class
using System;
using System.Threading;
public class ThreadSafe
{
// totalValue contains a running total that can be updated
// by multiple threads. It must be protected from unsynchronized
// access.
private double totalValue = 0;
// The Total property returns the running total.
public double Total
{
get { return totalValue; }
}
// AddToTotal safely adds a value to the running total.
public double AddToTotal(double addend)
{
double initialValue, computedValue;
do
{
// Save the current running total in a local variable.
initialValue = totalValue;
// Add the new value to the running total.
computedValue = initialValue + addend;
// CompareExchange compares totalValue to initialValue. If
// they are not equal, then another thread has updated the
// running total since this loop started. CompareExchange
// does not update totalValue. CompareExchange returns the
// contents of totalValue, which do not equal initialValue,
// so the loop executes again.
}
while (initialValue != Interlocked.CompareExchange(
ref totalValue, computedValue, initialValue));
// If no other thread updated the running total, then
// totalValue and initialValue are equal when CompareExchange
// compares them, and computedValue is stored in totalValue.
// CompareExchange returns the value that was in totalValue
// before the update, which is equal to initialValue, so the
// loop ends.
// The function returns computedValue, not totalValue, because
// totalValue could be changed by another thread between
// the time the loop ends and the function returns.
return computedValue;
}
}
using namespace System;
using namespace System::Threading;
public ref class ThreadSafe
{
// totalValue contains a running total that can be updated
// by multiple threads. It must be protected from unsynchronized
// access.
private:
double static totalValue = 0;
public:
// The Total property returns the running total.
property double Total
{
double get() { return totalValue; }
}
// AddToTotal safely adds a value to the running total.
double AddToTotal(double addend)
{
double initialValue, computedValue;
do
{
// Save the current running total in a local variable.
initialValue = totalValue;
// Add the new value to the running total.
computedValue = initialValue + addend;
// CompareExchange compares totalValue to initialValue. If
// they are not equal, then another thread has updated the
// running total since this loop started. CompareExchange
// does not update totalValue. CompareExchange returns the
// contents of totalValue, which do not equal initialValue,
// so the loop executes again.
}
while (initialValue != Interlocked::CompareExchange(
totalValue, computedValue, initialValue));
// If no other thread updated the running total, then
// totalValue and initialValue are equal when CompareExchange
// compares them, and computedValue is stored in totalValue.
// CompareExchange returns the value that was in totalValue
// before the update, which is equal to initialValue, so the
// loop ends.
// The function returns computedValue, not totalValue, because
// totalValue could be changed by another thread between
// the time the loop ends and the function returns.
return computedValue;
}
};
Sem tipo sobrecargas do Exchange e CompareExchange
O Exchange e CompareExchange métodos têm sobrecargas que levam argumentos do tipo Object. O primeiro argumento de cada um desses sobrecargas é ref Object (ByRef … As Object em Visual Basic), e o tipo de segurança requer a variável passada para este argumento seja digitado estritamente como Object; Você simplesmente não é possível converter o primeiro argumento, digite Object ao chamar esses métodos.
Observação
No.NET Framework versão 2.0, use os métodos sobrecarregados genéricos da Exchange e CompareExchange métodos de troca altamente digitado variáveis.
O exemplo de código a seguir mostra uma propriedade do tipo ClassA que podem ser definidas somente uma vez, conforme pode ser implementado na.NET Framework versão 1.0 ou 1.1.
Public Class ClassB
' The private field that stores the value for the
' ClassA property is intialized to null. It is set
' once, from any of several threads. The field must
' be of type Object, so that CompareExchange can be
' used to assign the value. If the field is used
' within the body of class Test, it must be cast to
' type ClassA.
Private classAValue As Object = Nothing
' This property can be set once to an instance of
' ClassA. Attempts to set it again cause an
' exception to be thrown.
Public Property ClassA() As ClassA
Get
Return CType(classAValue, ClassA)
End Get
Set
' CompareExchange compares the value in classAValue
' to null. The new value assigned to the ClassA
' property, which is in the special variable 'value',
' is placed in classAValue only if classAValue is
' equal to null.
If Not (Nothing Is Interlocked.CompareExchange(classAValue, _
CType(value, [Object]), Nothing)) Then
' CompareExchange returns the original value of
' classAValue; if it is not null, then a value
' was already assigned, and CompareExchange did not
' replace the original value. Throw an exception to
' indicate that an error occurred.
Throw New ApplicationException("ClassA was already set.")
End If
End Set
End Property
End Class
public class ClassB
{
// The private field that stores the value for the
// ClassA property is intialized to null. It is set
// once, from any of several threads. The field must
// be of type Object, so that CompareExchange can be
// used to assign the value. If the field is used
// within the body of class Test, it must be cast to
// type ClassA.
private object classAValue = null;
// This property can be set once to an instance of
// ClassA. Attempts to set it again cause an
// exception to be thrown.
public ClassA ClassA
{
get
{
return (ClassA) classAValue;
}
set
{
// CompareExchange compares the value in classAValue
// to null. The new value assigned to the ClassA
// property, which is in the special variable 'value',
// is placed in classAValue only if classAValue is
// equal to null.
if (null != Interlocked.CompareExchange(ref classAValue,
(Object) value, null))
{
// CompareExchange returns the original value of
// classAValue; if it is not null, then a value
// was already assigned, and CompareExchange did not
// replace the original value. Throw an exception to
// indicate that an error occurred.
throw new ApplicationException("ClassA was already set.");
}
}
}
}
public ref class ClassB
{
// The private field that stores the value for the
// ClassA property is intialized to null. It is set
// once, from any of several threads. The field must
// be of type Object, so that CompareExchange can be
// used to assign the value. If the field is used
// within the body of class Test, it must be cast to
// type ClassA.
private:
static Object^ classAValue = nullptr;
// This property can be set once to an instance of
// ClassA. Attempts to set it again cause an
// exception to be thrown.
public:
property ClassA^ classA
{
ClassA^ get()
{
return (ClassA^) classAValue;
}
void set(ClassA^ value)
{
// CompareExchange compares the value in classAValue
// to null. The new value assigned to the ClassA
// property, which is in the special variable 'value',
// is placed in classAValue only if classAValue is
// equal to null.
if (nullptr != Interlocked::CompareExchange(classAValue,
(Object^) value, nullptr))
{
// CompareExchange returns the original value of
// classAValue; if it is not null, then a value
// was already assigned, and CompareExchange did not
// replace the original value. Throw an exception to
// indicate that an error occurred.
throw gcnew ApplicationException("ClassA was already set.");
}
}
}
};