¿Cómo usar un AppDomain para limitar el alcance de una clase estática para un uso seguro de subprocesos?

StackOverflow https://stackoverflow.com/questions/307292

Pregunta

Me ha mordido una solución mal diseñada. ¡No es seguro para hilos!

Tengo varias clases y miembros compartidos en la solución, y durante el desarrollo todo fue genial ...
BizTalk ha hundido mi barco de batalla.

Estamos utilizando un adaptador BizTalk personalizado para llamar a mis ensamblados. El adaptador está llamando a mi código y está ejecutando cosas en paralelo, por lo que supongo que está usando varios subprocesos todos bajo el mismo dominio de aplicación.

Lo que me gustaría hacer es hacer que mi código se ejecute bajo su propio dominio de aplicación para que los problemas compartidos que tengo no se acumulen entre sí.

Tengo una clase muy simple que el adaptador BizTalk está creando instancias y luego ejecuta un método Process ().

Me gustaría crear un nuevo AppDomain dentro de mi método Process (), por lo que cada vez que BizTalk gire otro hilo, tendrá su propia versión de las clases y métodos estáticos.

Código BizTalkAdapter:

  // this is inside the BizTalkAdapter and it is calling the Loader class //
  private void SendMessage(IBaseMessage message, TransactionalTransmitProperties properties)
    {

        Stream strm = message.BodyPart.GetOriginalDataStream();
        string connectionString = properties.ConnectionString;
        string msgFileName = message.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties") as string;


        Loader loader = new Loader(strm, msgFileName, connectionString);
        loader.Process();

        EventLog.WriteEntry("Loader", "Successfully processed: " + msgFileName);

    }

Esta es la clase Llamadas de BizTalk:

public class Loader
{

    private string connectionString;
    private string fileName;
    private Stream stream;
    private DataFile dataFile;

    public Loader(Stream stream, string fileName, string connectionString)
    {
        this.connectionString = connectionString;
        this.fileName = fileName;
        this.stream = stream;
    }  

    public void Process()
    {

        //*****  Create AppDomain HERE *****
        // run following code entirely under that domain
        dataFile = new DataFile(aredStream, fileName, connectionString);
        dataFile.ParseFile();
        dataFile.Save();
        // get rid of the AppDomain here...

    }

}

FYI: La clase Loader está en un archivo DLL separado de la clase dataFile.

Cualquier ayuda sería apreciada. Continuaré trabajando para hacer que el código sea seguro para subprocesos, pero siento que esto podría ser el "simple". responder.

Si alguien tiene alguna otra idea, por favor tírela.

Gracias,
Keith

  

Solo para completar.

     

Encontré que si marcaba el adaptador de envío como " Entrega ordenada " en   las "Opciones avanzadas de transporte" diálogo pude evitar el   problemas de subprocesos múltiples que tenía.

     

Calculo que esta es otra posible respuesta a mi problema, pero no   necesariamente a la pregunta.

¿Fue útil?

Solución

Usando dominios de aplicación, podría hacer algo como esto:

public class Loader
{

    private string connectionString;
    private string fileName;
    private Stream stream;
    private DataFile dataFile;

    public Loader(Stream stream, string fileName, string connectionString)
    {
        this.connectionString = connectionString;
        this.fileName = fileName;
        this.stream = stream;
    }  

    public void Process()
    {
        //*****  Create AppDomain HERE *****
        string threadID = Thread.CurrentThread.ManagedThreadId.ToString();
        AppDomain appDomain = AppDomain.CreateDomain(threadID);

        DataFile dataFile = 
            (DataFile) appDomain.CreateInstanceAndUnwrap(
                        "<DataFile AssemblyName>", 
                        "DataFile", 
                        true, 
                        BindingFlags.Default,
                        null,
                        new object[] 
                        { 
                            aredstream, 
                            filename, 
                            connectionString 
                        },
                        null,
                        null,
                        null);
        dataFile.ParseFile();
        dataFile.Save();

        appDomain.Unload(threadID);       
    }
}

Otros consejos

¿Qué parte, exactamente, es ser una molestia en términos de seguridad del hilo? No puedo ver ningún estado estático ni singletons, y parece ser apropiado "nuevo" objetos ... ¿estoy siendo ciego?

Entonces, ¿cuál es el síntoma que estás viendo ...

Una respuesta de AppDomain será (relativamente) lenta. Como parte de un sistema respaldado por middleware, esto podría estar bien (es decir, el "relativamente" está en el mismo estadio).

Si do tiene algún estado estático en alguna parte, otra opción que a veces funciona es [ThreadStatic], que el tiempo de ejecución interpreta como "este campo estático es único por hilo". Sin embargo, debe tener cuidado con la inicialización: el constructor estático en el subproceso A podría asignar un campo, pero luego el subproceso B vería un nulo / 0 / etc.

¿Por qué no simplemente bloquear el código que desea ejecutar secuencialmente? Será un cuello de botella, pero debería funcionar en un entorno multiproceso.

public class Loader
{
    private static object SyncRoot = new object();
    private string connectionString;
    private string fileName;
    private Stream stream;
    private DataFile dataFile;

    public Loader(Stream stream, string fileName, string connectionString)
    {
        this.connectionString = connectionString;
        this.fileName = fileName;
        this.stream = stream;
    }  

    public void Process()
    {

        lock(SyncRoot) {
            dataFile = new DataFile(aredStream, fileName, connectionString);
            dataFile.ParseFile();
           dataFile.Save();
        }

    }

}

Si ha compartido estadísticas que están en conflicto entre sí, puede intentar agregarles el atributo [ThreadStatic]. Esto los hará locales a cada hilo. Eso puede resolver su problema a corto plazo. Una solución correcta sería simplemente volver a diseñar sus cosas para que sean seguras para subprocesos.

Solo para completar.

Encontré que si marcaba el adaptador de envío como " Entrega ordenada " en las "Opciones avanzadas de transporte" diálogo pude evitar los problemas de subprocesos múltiples que tenía.

Creo que esta es otra posible respuesta a mi problema, pero no necesariamente a la pregunta.

Creación y eliminación de un dominio de aplicación para cada llamada. ¿Supongo que no le preocupa el rendimiento en esta?

Idealmente, debe cambiar el código llamado para que sea seguro para subprocesos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top