No conozco un artículo o libro que aborde lo que está buscando, así que aquí están mis "lecciones aprendidas" de 12 años de depuración multiproceso en Windows (tanto no administrado como administrado).
Como mencioné en mi comentario, la mayor parte de mi "depuración multiproceso" se realiza realmente a través de una revisión de código manual, buscando estos problemas.
puntos muertos y dañado el estado compartido
Documento lock hierarchies (tanto en el orden y en qué estado se protegen compartida), y asegurarse de que son coherentes. Esto resuelve la mayoría de los problemas de interbloqueo y problemas de estado compartido corruptos.
(Nota: el enlace de arriba para "bloquear jerarquías" se refiere a un artículo del Dr. Dobbs por Herb Sutter, que ha escrito toda una serie de artículos Effective Concurrency que recomiendo).
Más sobre interbloqueos
Use RAII for all synchronization. Esto asegura que los bloqueos se lanzan frente a las excepciones. Prefiere la declaración de "bloqueo" para probar/finalmente.
(Tenga en cuenta que RAII en .NET depende de IDisposable
, no Finalize
, y se supone que el código de cliente usará correctamente un bloque using
).
inanición
Retire cualquier modificación de las prioridades de rosca. La correcta priorización es en realidad un poco contra-intuitiva: es mejor dar al subproceso con más trabajo para hacer una prioridad más baja, y dar mayor prioridad a los hilos que están vinculados con E/S (incluido el subproceso de interfaz de usuario).Como Windows hace esto automáticamente (vea Windows Internals), realmente no hay ninguna razón para que el código se involucre en absoluto.
En general
eliminar todo el código de bloqueo sin que se escribió en el local. Es casi seguro que contiene errores sutiles. Reemplácelo con .NET 4 lock-free collections y synchronization objects, o cambie el código para que esté basado en el bloqueo.
Utilice conceptos de nivel superior para la sincronización. La casi cualquier necesidad Task Parallel Library y unified cancellation en .NET 4 Retire para el uso directo de la ManualResetEvent
, Monitor
, Semaphore
, etc.
conceptos uso de más alto nivel para la paralelización. El TPL and PLINQ en .NET 4 tiene algoritmos de autoequilibrado integrados con particiones inteligentes y colas de robo de trabajo para proporcionar una paralelización óptima de forma automática. Para los pocos casos en que la paralelización automática no es óptima, tanto TPL como PLINQ exponen una gran cantidad de perillas ajustables (esquemas de particionamiento personalizados, indicadores de operación de larga ejecución, etc.).
Hay una técnica más que he encontrado útil para cualquier clase que tiene sus métodos llamados por diferentes subprocesos: documentar qué métodos se ejecutan en qué subprocesos. Por lo general, esto se agrega como un comentario en la parte superior del método. Asegúrese de que cada método solo se ejecute en un contexto de subproceso conocido (por ejemplo, "en un subproceso de UI" o "en un subproceso de ThreadPool" o "en el subproceso de fondo dedicado"). Ninguno de los métodos debe decir "en ningún hilo" a menos que esté escribiendo una clase de sincronización (y si está escribiendo una clase de sincronización, pregúntese si realmente debería estar haciendo eso).
Por último, nombre sus hilos. Esto ayuda a distinguirlos fácilmente cuando se utiliza el depurador VS. .NET admite esto a través de la propiedad .
Existen muchas herramientas geniales, pero no pase por alto el simple enfoque de simplemente leer el código. He estado haciendo depuración multihilo durante años, y es uno de los métodos más efectivos. –
Es cierto. Eso funciona bastante bien. Estoy buscando más técnicas ('me gusta' esto) que herramientas. – Mau
+1 @Stephen Muy cierto de hecho. –