Estoy ampliando mis comentarios en una respuesta porque creo que algunos aspectos del problema específico se pasan por alto o se utilizan para sacar conclusiones erróneas.
En este punto, la cuestión de si refactorizar es prematura (aunque probablemente sea respondida por una forma específica de 'sí').
El problema central aquí es que (como se señala en algunas respuestas) los comentarios que cita indican fuertemente que el código tiene condiciones de carrera u otros problemas de concurrencia / sincronización, como se discute aquí . Estos son problemas particularmente difíciles, por varias razones. En primer lugar, como ha encontrado, los cambios aparentemente no relacionados pueden desencadenar el problema (otros errores también pueden tener este efecto, pero los errores de concurrencia casi siempre lo hacen). En segundo lugar, son muy difíciles de diagnosticar: el error a menudo se manifiesta en un lugar que es distante en el tiempo o el código de la causa, y cualquier cosa que haga para diagnosticarlo puede hacer que desaparezca ( Heisenbugs) En tercer lugar, los errores de concurrencia son muy difíciles de encontrar en las pruebas. En parte, eso se debe a la explosión combinatoria: es lo suficientemente malo para el código secuencial, pero agregar los posibles entrelazados de ejecución simultánea lo lleva al punto en que el problema secuencial se vuelve insignificante en comparación. Además, incluso un buen caso de prueba solo puede desencadenar el problema ocasionalmente: Nancy Leveson calculó que uno de los errores letales en Therac 25ocurrió en 1 de aproximadamente 350 ejecuciones, pero si no sabe cuál es el error, o incluso si hay uno, no sabe cuántas repeticiones hacen una prueba efectiva. Además, solo las pruebas automáticas son factibles a esta escala, y es posible que el controlador de prueba imponga restricciones de tiempo sutiles de modo que nunca active el error (Heisenbugs nuevamente).
Hay algunas herramientas para la prueba de concurrencia en algunos entornos, como Helgrind para el código utilizando hilos POSIX, pero no conocemos los detalles aquí. Las pruebas deben complementarse con análisis estático (¿o es al revés?), Si existen herramientas adecuadas para su entorno.
Para aumentar la dificultad, los compiladores (e incluso los procesadores, en tiempo de ejecución) a menudo son libres de reorganizar el código de maneras que a veces hacen que el razonamiento sobre la seguridad de los subprocesos sea muy intuitivo (quizás el caso más conocido es la verificación doble modismo de bloqueo , aunque algunos entornos (Java, C ++ ...) se han modificado para mejorarlo).
Este código puede tener un problema simple que está causando todos los síntomas, pero es más probable que tenga un problema sistémico que podría detener sus planes de agregar nuevas funciones. Espero haberte convencido de que podrías tener un problema grave en tus manos, posiblemente incluso una amenaza existencial para tu producto, y lo primero que debes hacer es descubrir qué está sucediendo. Si esto revela problemas de concurrencia, le recomiendo encarecidamente que los solucione primero, incluso antes de preguntar si debería realizar una refactorización más general, y antes de intentar agregar más funciones.