¿Cómo cambio atómicamente 2 ints en C #?

¿Qué (si existe) es el equivalente de C # de la instrucción x86 asm xchg ?

Con ese comando, que es un intercambio genuino (a diferencia de Interlocked.Exchange ), podría simplemente cambiar atómicamente dos ints, que es lo que realmente estoy tratando de hacer.

Actualizar:

Código de muestra basado en mi sugerencia. Los sufijos variables “_V” están decorados como volátiles:

 // PART 3 - process links // prepare the new Producer address.ProducerNew.WorkMask_V = 0; // copy the current LinkMask address.ProducerNew.LinkMask_V = address.Producer.LinkMask_V; // has another (any) thread indicated it dropped its message link from this thread? if (this.routerEmptyMask[address.ID] != 0) { // allow all other bits to remain on (ie turn off now defunct links) address.ProducerNew.LinkMask_V &= ~this.routerEmptyMask[address.ID]; // reset this.routerEmptyMask[address.ID] = 0; } // PART 4 - swap address.ProducerNew = Interlocked.Exchange(ref address.Producer, address.ProducerNew); // PART 5 - lazily include the new links, make a working copy workMask = address.Producer.LinkMask_V |= address.ProducerNew.WorkMask_V; 

Tenga en cuenta la actualización perezosa .

Aquí hay una especie de idea extraña. No sé exactamente cómo configuró su estructura de datos. Pero si es posible que puedas almacenar tus dos valores int en un long , entonces creo que podrías intercambiarlos atómicamente.

Por ejemplo, supongamos que ha ajustado sus dos valores de la siguiente manera:

 class SwappablePair { long m_pair; public SwappablePair(int x, int y) { m_pair = ((long)x << 32) | (uint)y; } ///  /// Reads the values of X and Y atomically. ///  public void GetValues(out int x, out int y) { long current = Interlocked.Read(ref m_pair); x = (int)(current >> 32); y = (int)(current & 0xffffffff); } ///  /// Sets the values of X and Y atomically. ///  public void SetValues(int x, int y) { // If you wanted, you could also take the return value here // and set two out int parameters to indicate what the previous // values were. Interlocked.Exchange(ref m_pair, ((long)x << 32) | (uint)y); } } 

Entonces parece que podrías agregar el siguiente método Swap para dar como resultado un par intercambiado "atómicamente" (en realidad, no sé si es justo decir realmente que el siguiente es atómico, es más como que produce el mismo resultado que un atómico). intercambio ).

 ///  /// Swaps the values of X and Y atomically. ///  public void Swap() { long orig, swapped; do { orig = Interlocked.Read(ref m_pair); swapped = orig << 32 | (uint)(orig >> 32); } while (Interlocked.CompareExchange(ref m_pair, swapped, orig) != orig); } 

Es muy posible que lo haya implementado de forma incorrecta, por supuesto. Y podría haber un error en esta idea. Es solo una idea

Esta es la posible implementación de Interlocked.Exchange () en el CLR, copiado de la fuente SSCLI20:

Tenga en cuenta que UP en el nombre de la función significa UniProcessor. Esto no es atómico en sistemas SMP / multi-core. Esta implementación solo será utilizada por CLR en sistemas de un solo núcleo.

 FASTCALL_FUNC ExchangeUP,8 _ASSERT_ALIGNED_4_X86 ecx mov eax, [ecx] ; attempted comparand retry: cmpxchg [ecx], edx jne retry1 ; predicted NOT taken retn retry1: jmp retry FASTCALL_ENDFUNC ExchangeUP 

Es superior al uso de XCHG porque este código funciona sin tomar un locking de bus. xchg tiene un prefijo de lock implícito, por lo que a diferencia de xadd o cmpxchg , simplemente no se puede omitir para que los sistemas de un solo núcleo realicen la operación en una instrucción para convertirla en atómica con respecto a las interrupciones (y por lo tanto otros subprocesos en uniprocesador).

El código de salto de aspecto extraño es una optimización en caso de que los datos de predicción de bifurcación no estén disponibles. Huelga decir que tal vez, tratando de hacer un mejor trabajo que lo que ha sido reflexionado durante muchos años por muy buenos ingenieros de software con generosas aportaciones de los fabricantes de chips, es una gran tarea.

¿Por qué no está Interlocked.Exchange adecuado para usted?

Si necesita cambiar las ubicaciones exactas de la memoria, está utilizando el lenguaje y la plataforma equivocados, ya que .NET abstrae la administración de la memoria para que no tenga que pensar en ello.

Si debe hacer algo como esto sin Interlocked.Exchange , podría escribir algún código marcado como unsafe y hacer un intercambio tradicional basado en un puntero como lo haría en C o C ++, pero tendría que envolverlo en un contexto de sincronización adecuado para que es una operación atómica.

Actualizar
No necesita recurrir a unsafe código unsafe para realizar un intercambio de forma atómica. Puede ajustar el código en un contexto de sincronización para que sea atómico.

 lock (myLockObject) { var x = Interlocked.Exchange(a, b); Interlocked.Exchange(b, x); } 

Actualización 2
Si la sincronización no es una opción (como se indica en los comentarios), entonces creo que no tiene suerte. A medida que persigue una eficiencia no medida, es posible que desee concentrarse en otro lugar. Si el intercambio de dos valores enteros es un gran obstáculo de rendimiento, probablemente esté utilizando la plataforma incorrecta.

Interlocked.Exchange es realmente lo único que puedes hacer:

  var x = Interlocked.Exchange(a, b); Interlocked.Exchange(b, x); 

Tiene razón en que esto no será atómico, pero con una variable local utilizada, se le garantiza que los valores son consistentes siempre que se ejecuten ambas líneas. Sus otras opciones son un código inseguro (para usar punteros), usar p / invoke a una biblioteca nativa o rediseñar para que ya no sea necesario.

Según MSDN , Interlocked.Exchange es atómico.

Si no es adecuado para usted, podría implementar XCHG en una sección insegura utilizando C / C ++.

Interlocked.Exchange es la mejor manera de intercambiar dos valores int de una manera segura para subprocesos en c #.

Utilice esta clase, incluso si tiene una computadora multiprocesador y nunca sabe en qué procesador se ejecutará su subproceso.

Creo que encontré la mejor solución. Es:

Interlocked.Exchange () Método (ref T, T)

Todas las variables “nuevas” se pueden configurar en una clase (De T) y se pueden intercambiar con las variables actuales. Esto permite que se produzca una instantánea atómica, al tiempo que se intercambia efectivamente cualquier cantidad de variables.

Fuera de Interlocked.Exchange, supongo que el comando XCHG es probablemente una implementación del XOR Swap , por lo que podría escribir el suyo.

Sintaxis C: (del enlace wikipedia)

  void xorSwap (int *x, int *y) { if (x != y) { *x ^= *y; *y ^= *x; *x ^= *y; } } 

Edita esto no es atómico, tendrías que sincronizarlo tú mismo