Comment utiliser un AppDomain pour limiter la portée d'une classe statique à une utilisation thread-safe?

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

Question

J'ai été piqué par une solution mal conçue. Ce n'est pas thread-safe!

J'ai plusieurs classes et membres partagés dans la solution, et pendant le développement, tout était cool ...
BizTalk a coulé mon navire de combat.

Nous utilisons un adaptateur BizTalk personnalisé pour appeler mes assemblys. L'adaptateur appelle mon code et exécute des tâches en parallèle. Je suppose donc qu'il utilise plusieurs threads, tous sous le même AppDomain.

Ce que je voudrais, c’est que mon code soit exécuté sous son propre AppDomain afin que les problèmes partagés que je rencontre ne se déforment pas.

J'ai une classe très simple que l'adaptateur BizTalk instancie puis exécute une méthode Process ().

J'aimerais créer un nouvel AppDomain dans ma méthode Process () afin que chaque fois que BizTalk crée un autre thread, il aura sa propre version des classes et méthodes statiques.

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

    }

C’est la classe des appels 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 se trouve dans une DLL distincte de la classe dataFile.

Toute aide serait appréciée. Je vais continuer à travailler sur le code Thread-Safe, mais j’ai l’impression que cela pourrait être le "simple" réponse.

Si quelqu'un a une autre pensée, lancez-vous.

Merci,
Keith

  

Juste pour être complet.

     

J'ai constaté que, si je définissais l'adaptateur d'envoi comme suit: "Livraison commandée". dans   les "Options avancées de transport" dialogue j'ai pu éviter la   problèmes multi-thread que je rencontrais.

     

Je pense que ceci est une autre réponse possible à mon problème, mais pas   nécessairement à la question.

Était-ce utile?

La solution

En utilisant des domaines d'application, vous pouvez faire quelque chose comme ceci:

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

Autres conseils

Quel bit, au juste, est-il une douleur en termes de sécurité du fil? Je ne vois aucun état statique ni singleton - et il semble y avoir un "nouveau" & approprié; objets ... suis-je aveugle?

Alors, quel est le symptôme que vous voyez ...

Une réponse AppDomain sera (relativement) lente. En tant que partie d’un système supporté par un middleware, cela pourrait être OK (c’est-à-dire que le groupe "relativement" se trouve dans le même parc de base-ball).

Si vous avez un état statique quelque part, une autre option qui fonctionne parfois est [ThreadStatic] - ce que le moteur d'exécution interprète comme "ce champ statique est unique par thread". Vous devez cependant faire attention à l'initialisation - le constructeur statique sur le thread A peut affecter un champ, mais alors le thread B verra un null / 0 / etc.

Pourquoi ne pas simplement verrouiller le code que vous voulez exécuter séquentiellement? Ce sera un goulot d’étranglement, mais cela devrait fonctionner dans un environnement 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();
        }

    }

}

Si vous avez des statiques partagées en conflit, essayez d’y ajouter l’attribut [ThreadStatic]. Cela les rend local à chaque thread. Cela peut résoudre votre problème à court terme. Une solution correcte consisterait simplement à faire une nouvelle recherche de votre contenu pour qu'il soit thread-safe.

Juste pour être complet.

J'ai constaté que, si je définissais l'adaptateur d'envoi comme suit: "Livraison commandée". dans les "Options avancées de transport". dialogue, j’ai pu éviter les problèmes de multi-threads que je rencontrais.

Je pense que ceci est une autre réponse possible à mon problème, mais pas nécessairement à la question.

Créer et démonter une application pour chaque appel - je suppose que vous n'êtes pas inquiet des performances de cet appel?

Idéalement, vous devriez changer le code appelé pour qu'il soit threadsafe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top