Come utilizzare un AppDomain per limitare l'ambito di una classe statica per un utilizzo thread-safe?

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

Domanda

Sono stato morso da una soluzione mal progettata. Non è thread-safe!

Ho diverse classi e membri condivisi nella soluzione e durante lo sviluppo tutto è stato bello ...
BizTalk ha affondato la mia nave da battaglia.

Stiamo usando un adattatore BizTalk personalizzato per chiamare i miei assembly. L'adapter chiama il mio codice e esegue le cose in parallelo, quindi suppongo che stia utilizzando più thread tutti nello stesso AppDomain.

Quello che vorrei fare è far funzionare il mio codice con il suo AppDomain in modo che i problemi condivisi che ho non si confondano.

Ho una classe molto semplice che l'adattatore BizTalk sta istanziando quindi eseguendo un metodo Process ().

Vorrei creare un nuovo AppDomain all'interno del mio metodo Process (), quindi ogni volta che BizTalk gira un altro thread, avrà la sua versione delle classi e dei metodi statici.

Codice 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);

    }

Questa è la classe Chiamate 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 classe Loader si trova in una DLL separata dalla classe dataFile.

Qualsiasi aiuto sarebbe apprezzato. Continuerò a lavorare per rendere il codice Thread-Safe, ma penso che questo potrebbe essere il "semplice" risposta.

Se qualcuno ha qualche altra idea, ti preghiamo di inserire.

Grazie,
Keith

  

Solo per completezza.

     

Ho scoperto che se contrassegnavo l'adattatore di invio come " Consegna ordinata " nel   le "Opzioni avanzate di trasporto" dialogo sono stato in grado di evitare il   problemi multi-thread che stavo riscontrando.

     

Immagino che questa sia un'altra possibile risposta al mio problema, ma non   necessariamente alla domanda.

È stato utile?

Soluzione

Usando i domini delle app potresti fare qualcosa del genere:

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);       
    }
}

Altri suggerimenti

Quale bit, esattamente, è un dolore in termini di sicurezza del thread? Non riesco a vedere alcuno stato statico né singleton - e sembra che ci siano opportuni "nuovi" oggetti ... sono cieco?

Quindi qual è il sintomo che stai vedendo ...

Una risposta AppDomain sarà (relativamente) lenta. Come parte di un sistema supportato da middleware, questo potrebbe essere OK (vale a dire che "relativamente" si trova nello stesso parco giochi).

Se hai uno stato statico da qualche parte, un'altra opzione che a volte funziona è [ThreadStatic] - che il runtime interpreta come "questo campo statico è unico per thread". Tuttavia, devi fare attenzione con l'inizializzazione: il costruttore statico sul thread A potrebbe assegnare un campo, ma il thread B visualizzerà un null / 0 / etc.

Perché non mettere semplicemente un lucchetto attorno al codice che vuoi eseguire in sequenza? Sarà un collo di bottiglia, ma dovrebbe funzionare in un ambiente multithread.

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();
        }

    }

}

Se hai statici condivisi in conflitto tra loro, potresti provare ad aggiungere l'attributo [ThreadStatic]. Questo li renderà locali per ogni thread. Ciò potrebbe risolvere il tuo problema a breve termine. Una soluzione corretta sarebbe semplicemente rearchitect le tue cose per essere thread-safe.

Solo per completezza.

Ho scoperto che se contrassegnavo l'adattatore di invio come " Consegna ordinata " nelle "Opzioni avanzate di trasporto" finestra di dialogo sono stato in grado di evitare i problemi multi-thread che stavo riscontrando.

Immagino che questa sia un'altra possibile risposta al mio problema, ma non necessariamente alla domanda.

Creazione e abbattimento di un dominio per ogni chiamata: suppongo che non ti preoccupi delle prestazioni su questo?

Idealmente dovresti cambiare il codice chiamato in thread-safe.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top