Tengo un código en el que se lanzan excepciones en un nuevo subproceso que necesito reconocer y tratar en el subproceso principal. Para lograr esto, estoy compartiendo el estado entre subprocesos mediante el uso de un campo que contiene la excepción lanzada.
Mi pregunta es : ¿necesito usar bloqueos cuando busco valores nulos como lo hago en el siguiente ejemplo de código?
public class MyClass { readonly object _exceptionLock = new object(); Exception _exception; public MyClass() { Task.Run(() => { while (CheckIsExceptionNull()) { // This conditional will return true if 'something has gone wrong'. if(CheckIfMyCodeHasGoneWrong()) { lock(_exceptionLock) { _exception = new GoneWrongException(); } } } }); } bool CheckIsExceptionNull() // Is this method actually necessary? { lock (_exceptionLock) { return _exception == null; } } // This method gets fired periodically on the Main Thread. void RethrowExceptionsOnMainThread() { if (!CheckIsExceptionNull()) { lock (_exceptionLock) { throw _exception; // Does this throw need to be in a lock? } } } }Además, ¿necesito usar un candado al lanzar la excepción en el subproceso principal?
Lo primero que debe tener en cuenta es que su código no es seguro para subprocesos porque tiene una carrera de subprocesos: verifica CheckIsExceptionNull en una región bloqueada diferente a donde lanza, pero: el valor puede cambiar entre la prueba y el lanzamiento .
No se garantiza que el acceso al campo sea seguro para subprocesos. En particular, si bien las referencias no se pueden romper (eso está garantizado: las lecturas y escrituras de referencias son atómicas), no se garantiza que diferentes subprocesos vean los valores más recientes, debido al almacenamiento en caché de la CPU, etc. Es muy poco probable que te muerda. , pero ese es el problema con los problemas de subprocesos en el caso general; p
Personalmente, probablemente solo haría que el campo fuera volátil y usaría un local. Por ejemplo:
var tmp = _exception; if(tmp != null) throw tmp;Lo anterior no tiene carrera de hilo. Agregando:
volatile Exception _exception; asegura que el valor no se almacene en caché en un registro (aunque eso es técnicamente un efecto secundario, no el efecto previsto/documentado de volatile )
Puede haber mucha información interesante y útil sobre preguntas como esta. Puede pasar horas leyendo publicaciones de blog y libros al respecto. Incluso las personas que realmente lo conocen mejor no están de acuerdo. (Como podéis ver en el post de Marc Gravell y Eric Lippert).
Así que no hay una respuesta a esta pregunta de mí. Solo una sugerencia: use siempre candados. Tan pronto como más de un subproceso esté accediendo a un campo. Sin riesgo, sin adivinar, sin discutir sobre compilador y tecnología de CPU. Simplemente use bloqueos y ahorre y haga que funcione no solo en la mayoría de los casos en su máquina, sino en todos los casos en todas las máquinas.
Otras lecturas: