Je démarre plusieurs threads via des QueueUserWorkItem. Dans chaque thread,
je souhaite compiler une assembly via system.codedom.compiler. Le résultat
obtenu (quand il y a résultat) n'est pas associé au bon thread. Comment
faire pour que le thread attende la compilation de l'assembly et l'éxécution
de la méthode et surtout pour qu'un autre thread n'y accède pas ? J'ai
essayer via un monitor mais ça ne fonctionne pas. Quelqu'un à une idée ?
Cette action est irreversible, confirmez la suppression du commentaire ?
Signaler le commentaire
Veuillez sélectionner un problème
Nudité
Violence
Harcèlement
Fraude
Vente illégale
Discours haineux
Terrorisme
Autre
adebaene
Mikado wrote:
Salut à tous,
Je démarre plusieurs threads via des QueueUserWorkItem.
Ca ne démarre pas des threads : ca postes du travail à faire pour le pool de thread qui *éventuellement* démarrera de nouveaux threads (ou réutilisera des threads existants).
Dans chaque thread, je souhaite compiler une assembly via system.codedom.compiler. Le résul tat obtenu (quand il y a résultat) n'est pas associé au bon thread.
????? Quel résultat n'est pas "associé" au bon thread? QueueUserWorkItem prend comme delegate un WaitCallback, qui spécifie un type de retour void, donc il n'y a pas de valeur de retour. Tu cherches à récupérer quoi au juste?
Comment faire pour que le thread attende la compilation de l'assembly et l'éx écution de la méthode et surtout pour qu'un autre thread n'y accède pas ? J'ai essayer via un monitor mais ça ne fonctionne pas. Quelqu'un à une id ée ?
Décris plus clairement ton problème si tu veux une aide précise.
Arnaud MVP - VC
Mikado wrote:
Salut à tous,
Je démarre plusieurs threads via des QueueUserWorkItem.
Ca ne démarre pas des threads : ca postes du travail à faire pour le
pool de thread qui *éventuellement* démarrera de nouveaux threads (ou
réutilisera des threads existants).
Dans chaque thread,
je souhaite compiler une assembly via system.codedom.compiler. Le résul tat
obtenu (quand il y a résultat) n'est pas associé au bon thread.
????? Quel résultat n'est pas "associé" au bon thread?
QueueUserWorkItem prend comme delegate un WaitCallback, qui spécifie
un type de retour void, donc il n'y a pas de valeur de retour. Tu
cherches à récupérer quoi au juste?
Comment
faire pour que le thread attende la compilation de l'assembly et l'éx écution
de la méthode et surtout pour qu'un autre thread n'y accède pas ? J'ai
essayer via un monitor mais ça ne fonctionne pas. Quelqu'un à une id ée ?
Décris plus clairement ton problème si tu veux une aide précise.
Je démarre plusieurs threads via des QueueUserWorkItem.
Ca ne démarre pas des threads : ca postes du travail à faire pour le pool de thread qui *éventuellement* démarrera de nouveaux threads (ou réutilisera des threads existants).
Dans chaque thread, je souhaite compiler une assembly via system.codedom.compiler. Le résul tat obtenu (quand il y a résultat) n'est pas associé au bon thread.
????? Quel résultat n'est pas "associé" au bon thread? QueueUserWorkItem prend comme delegate un WaitCallback, qui spécifie un type de retour void, donc il n'y a pas de valeur de retour. Tu cherches à récupérer quoi au juste?
Comment faire pour que le thread attende la compilation de l'assembly et l'éx écution de la méthode et surtout pour qu'un autre thread n'y accède pas ? J'ai essayer via un monitor mais ça ne fonctionne pas. Quelqu'un à une id ée ?
Décris plus clairement ton problème si tu veux une aide précise.
Arnaud MVP - VC
Mikado
Salut Arnaud,
A vouloir faire trop simpliste ça finit par plus vouloir rien dire. En gros, voici un peu le comportement dont j'ai besoin (c'est vraiment un exemple le code réel est beaucoup plus long)... une première procédure contient le genre de code suivant (je mets pas toutes les déclarations pour simplifier) :
Dim UnSafeUsers As New Queue Dim Users as Queue Users = Users.Synchronized(UnSafeUsers) 'Je rends thread safe une Collection d'utilisateurs
For User = 1 To 100 Users.Enqueue = "Utilisateur" & User 'Je mets en queue 100 utilisateurs Next
For Thread = 0 To 10 'Je souhaite déclencher 10 threads (à la condition bien sûr que le pool est 10 threads de libre) 'Les threads déclenchés doivent dequeue les 100 utilisateurs. En théorie chaque thread devrait dequeue 10 utilisateurs EventThread(Thread) = New ManualResetEvent(False) ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf MaClasse.MaFonction), New Params(Template, Users, EventThread(Thread)) Next If WaitHandle.WaitAll(EventThread) Then Trace.writeline("Ok s'est terminé")
La classe Params me permet de passer les différents paramètres à ma fonction via des champs. Ils tous passés par reférence.
Public Class MaClasse Public Count As Integer = 0 'Je veux compter le nombre d'utilisateur qui ont été dequeue
Public Sub MaFonction(ByVal Args As Object) Args = CType(Args, Params) 'Je récupère les différentes valeurs passées en paramètres via la classe Params Dim Template as String = Args.Template Dim Users as Queue = Args.Users Dim EventThread as ManualResetEvent = Args.EventThread Dim Result as string
Do While Users.Count > 0 'Tant que les threads n'ont pas terminé de vider la queue users... Result = Users.Dequeue 'Result contient pour le premier élément Utilisateur1, Utilisateur2 pour le second et ainsi de suite... Interlocked.Increment(Count) 'On incrémente de 1 le nombre d'utilisateur traité...
Dim CodeProvider As VBCodeProvider = New VBCodeProvider Dim ICC As ICodeCompiler = CodeProvider.CreateCompiler
Dim Parameters As CompilerParameters = New CompilerParameters Parameters.GenerateExecutable = False Parameters.GenerateInMemory = True
Dim SourceCode As String SourceCode = "Imports System" & vbCrLf SourceCode += "Namespace NameSpaceTest" & vbCrLf SourceCode += " Public Class ClassTest" & vbCrLf SourceCode += " Function RenderTest(ByVal User as string) as string" & vbCrLf SourceCode += " Return ""C'est l'utilisateur : "" & User" & vbCrLf SourceCode += " End Function" & vbCrLf SourceCode += " End Class" & vbCrLf SourceCode += "End Namespace"
Dim Results As CompilerResults ICC.CompileAssemblyFromSource(Parameters, SourceCode) Dim Instance As Object Results.CompiledAssembly.CreateInstance("NameSpaceTest.ClassTest") Dim TType As Type = Instance.GetType Dim Params As Object() = New Object(0) {} Params(0) = Result 'Dans mon code Result contient des données à traiter. Pour cet exemple j'y ai mis le résultat du Dequeue histoire de montrer le problème
Result = TType.InvokeMember("RenderTest", Reflection.BindingFlags.InvokeMethod, Nothing, Instance, Params) Trace.writeline("-->" & Result & " (" & Count & " / " & Users.count+1 & ")") Loop ThreadEvent.Set() End Sub End Class
Avec un thread et un utilisateur j'obtiens : --> C'est l'utilisateur : Utilisateur1 (1 / 1)
Avec un thread et plusieurs utilisateur j'obtiens : --> Utilisateur1 (1 / 5) --> Utilisateur2 (2 / 4) --> Utilisateur3 (3 / 3) --> Utilisateur4 (4 / 2) --> Utilisateur5 (5 / 1)
Je suppose qu'il s'agit d'un problème de synchro mais j'arrive pas à le corriger.
Plus bizarre, avec plusieurs threads et plusieurs utilisateurs j'obtiens :
Voilà en gros mon problème. Alors docteur... ou est-ce qu'il faut que je corrige ?
Merci
Mikado
Salut Arnaud,
A vouloir faire trop simpliste ça finit par plus vouloir rien dire. En gros,
voici un peu le comportement dont j'ai besoin (c'est vraiment un exemple le
code réel est beaucoup plus long)... une première procédure contient le
genre de code suivant (je mets pas toutes les déclarations pour simplifier)
:
Dim UnSafeUsers As New Queue
Dim Users as Queue
Users = Users.Synchronized(UnSafeUsers) 'Je rends thread safe une Collection
d'utilisateurs
For User = 1 To 100
Users.Enqueue = "Utilisateur" & User 'Je mets en queue 100 utilisateurs
Next
For Thread = 0 To 10 'Je souhaite déclencher 10 threads (à la condition bien
sûr que le pool est 10 threads de libre)
'Les threads déclenchés doivent dequeue les 100 utilisateurs. En théorie
chaque thread devrait dequeue 10 utilisateurs
EventThread(Thread) = New ManualResetEvent(False)
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf
MaClasse.MaFonction), New Params(Template, Users, EventThread(Thread))
Next
If WaitHandle.WaitAll(EventThread) Then Trace.writeline("Ok s'est terminé")
La classe Params me permet de passer les différents paramètres à ma fonction
via des champs. Ils tous passés par reférence.
Public Class MaClasse
Public Count As Integer = 0 'Je veux compter le nombre d'utilisateur qui
ont été dequeue
Public Sub MaFonction(ByVal Args As Object)
Args = CType(Args, Params) 'Je récupère les différentes valeurs
passées en paramètres via la classe Params
Dim Template as String = Args.Template
Dim Users as Queue = Args.Users
Dim EventThread as ManualResetEvent = Args.EventThread
Dim Result as string
Do While Users.Count > 0 'Tant que les threads n'ont pas terminé de
vider la queue users...
Result = Users.Dequeue 'Result contient pour le premier
élément Utilisateur1, Utilisateur2 pour le second et ainsi de suite...
Interlocked.Increment(Count) 'On incrémente de 1 le nombre
d'utilisateur traité...
Dim CodeProvider As VBCodeProvider = New VBCodeProvider
Dim ICC As ICodeCompiler = CodeProvider.CreateCompiler
Dim Parameters As CompilerParameters = New
CompilerParameters
Parameters.GenerateExecutable = False
Parameters.GenerateInMemory = True
Dim SourceCode As String
SourceCode = "Imports System" & vbCrLf
SourceCode += "Namespace NameSpaceTest" & vbCrLf
SourceCode += " Public Class ClassTest" & vbCrLf
SourceCode += " Function RenderTest(ByVal User as string) as
string" & vbCrLf
SourceCode += " Return ""C'est l'utilisateur : "" & User" &
vbCrLf
SourceCode += " End Function" & vbCrLf
SourceCode += " End Class" & vbCrLf
SourceCode += "End Namespace"
Dim Results As CompilerResults ICC.CompileAssemblyFromSource(Parameters, SourceCode)
Dim Instance As Object Results.CompiledAssembly.CreateInstance("NameSpaceTest.ClassTest")
Dim TType As Type = Instance.GetType
Dim Params As Object() = New Object(0) {}
Params(0) = Result 'Dans mon code Result contient des
données à traiter. Pour cet exemple j'y ai mis le résultat du Dequeue
histoire de montrer le problème
Result = TType.InvokeMember("RenderTest",
Reflection.BindingFlags.InvokeMethod, Nothing, Instance, Params)
Trace.writeline("-->" & Result & " (" & Count & " / " &
Users.count+1 & ")")
Loop
ThreadEvent.Set()
End Sub
End Class
Avec un thread et un utilisateur j'obtiens :
--> C'est l'utilisateur : Utilisateur1 (1 / 1)
Avec un thread et plusieurs utilisateur j'obtiens :
--> Utilisateur1 (1 / 5)
--> Utilisateur2 (2 / 4)
--> Utilisateur3 (3 / 3)
--> Utilisateur4 (4 / 2)
--> Utilisateur5 (5 / 1)
Je suppose qu'il s'agit d'un problème de synchro mais j'arrive pas à le
corriger.
Plus bizarre, avec plusieurs threads et plusieurs utilisateurs j'obtiens :
A vouloir faire trop simpliste ça finit par plus vouloir rien dire. En gros, voici un peu le comportement dont j'ai besoin (c'est vraiment un exemple le code réel est beaucoup plus long)... une première procédure contient le genre de code suivant (je mets pas toutes les déclarations pour simplifier) :
Dim UnSafeUsers As New Queue Dim Users as Queue Users = Users.Synchronized(UnSafeUsers) 'Je rends thread safe une Collection d'utilisateurs
For User = 1 To 100 Users.Enqueue = "Utilisateur" & User 'Je mets en queue 100 utilisateurs Next
For Thread = 0 To 10 'Je souhaite déclencher 10 threads (à la condition bien sûr que le pool est 10 threads de libre) 'Les threads déclenchés doivent dequeue les 100 utilisateurs. En théorie chaque thread devrait dequeue 10 utilisateurs EventThread(Thread) = New ManualResetEvent(False) ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf MaClasse.MaFonction), New Params(Template, Users, EventThread(Thread)) Next If WaitHandle.WaitAll(EventThread) Then Trace.writeline("Ok s'est terminé")
La classe Params me permet de passer les différents paramètres à ma fonction via des champs. Ils tous passés par reférence.
Public Class MaClasse Public Count As Integer = 0 'Je veux compter le nombre d'utilisateur qui ont été dequeue
Public Sub MaFonction(ByVal Args As Object) Args = CType(Args, Params) 'Je récupère les différentes valeurs passées en paramètres via la classe Params Dim Template as String = Args.Template Dim Users as Queue = Args.Users Dim EventThread as ManualResetEvent = Args.EventThread Dim Result as string
Do While Users.Count > 0 'Tant que les threads n'ont pas terminé de vider la queue users... Result = Users.Dequeue 'Result contient pour le premier élément Utilisateur1, Utilisateur2 pour le second et ainsi de suite... Interlocked.Increment(Count) 'On incrémente de 1 le nombre d'utilisateur traité...
Dim CodeProvider As VBCodeProvider = New VBCodeProvider Dim ICC As ICodeCompiler = CodeProvider.CreateCompiler
Dim Parameters As CompilerParameters = New CompilerParameters Parameters.GenerateExecutable = False Parameters.GenerateInMemory = True
Dim SourceCode As String SourceCode = "Imports System" & vbCrLf SourceCode += "Namespace NameSpaceTest" & vbCrLf SourceCode += " Public Class ClassTest" & vbCrLf SourceCode += " Function RenderTest(ByVal User as string) as string" & vbCrLf SourceCode += " Return ""C'est l'utilisateur : "" & User" & vbCrLf SourceCode += " End Function" & vbCrLf SourceCode += " End Class" & vbCrLf SourceCode += "End Namespace"
Dim Results As CompilerResults ICC.CompileAssemblyFromSource(Parameters, SourceCode) Dim Instance As Object Results.CompiledAssembly.CreateInstance("NameSpaceTest.ClassTest") Dim TType As Type = Instance.GetType Dim Params As Object() = New Object(0) {} Params(0) = Result 'Dans mon code Result contient des données à traiter. Pour cet exemple j'y ai mis le résultat du Dequeue histoire de montrer le problème
Result = TType.InvokeMember("RenderTest", Reflection.BindingFlags.InvokeMethod, Nothing, Instance, Params) Trace.writeline("-->" & Result & " (" & Count & " / " & Users.count+1 & ")") Loop ThreadEvent.Set() End Sub End Class
Avec un thread et un utilisateur j'obtiens : --> C'est l'utilisateur : Utilisateur1 (1 / 1)
Avec un thread et plusieurs utilisateur j'obtiens : --> Utilisateur1 (1 / 5) --> Utilisateur2 (2 / 4) --> Utilisateur3 (3 / 3) --> Utilisateur4 (4 / 2) --> Utilisateur5 (5 / 1)
Je suppose qu'il s'agit d'un problème de synchro mais j'arrive pas à le corriger.
Plus bizarre, avec plusieurs threads et plusieurs utilisateurs j'obtiens :
Voilà en gros mon problème. Alors docteur... ou est-ce qu'il faut que je corrige ?
Merci
Mikado
adebaene
Mikado wrote: <snip>>
Do While Users.Count > 0 Result = Users.Dequeue Interlocked.Increment(Count)
</snip>
Loop
Ceci n'est pas thread-safe du tout, même si Users est une liste "Synchronized". Cette idée de collections "Synchronized" est de toute façon foireuse (AMHA, c'est une mauvaise idée d'avoir introduit çà dans le framework au départ, ca implique trop de dangers que les gens se croient synchronisés alors qu'il ne le sont pas du tout). En effet, quasiement tout le temps, ce que tu veux rendre atomique, c'est un ensemble de plusieurs opérations sur le conteneur, et pas simplement une opération. Dans ton cas, il faut synhcroniser l'ensemble de la boucle (test du nombre d'élements de la queue et Dequeue). Ca donne quelque chose du genre :
Dim continue_loop as bool = true; Do While continue_loop SyncLock Me 'je syncrhonise *toute* l'operation de Dequeue continue_loop = Users.Count > 0 If continue_loop Result = Users.Dequeue Interlocked.Increment(Count) End If End SyncLock
' faire le traitement sur Result si continue_loop est vrai. ' Remarques qu'on peut faire ce traitement sans tenir le lock qui ne ' sert qu'à protéger la boucle.
Loop ThreadEvent.Set()
Désolé pour les fautes de syntaxe (VB n'est vraiement pas ma tasse de thé), mais ca te donne une idée.
Arnaud MVP - VC
Mikado wrote:
<snip>>
Do While Users.Count > 0
Result = Users.Dequeue
Interlocked.Increment(Count)
</snip>
Loop
Ceci n'est pas thread-safe du tout, même si Users est une liste
"Synchronized".
Cette idée de collections "Synchronized" est de toute façon foireuse
(AMHA, c'est une mauvaise idée d'avoir introduit çà dans le
framework au départ, ca implique trop de dangers que les gens se
croient synchronisés alors qu'il ne le sont pas du tout).
En effet, quasiement tout le temps, ce que tu veux rendre atomique,
c'est un ensemble de plusieurs opérations sur le conteneur, et pas
simplement une opération. Dans ton cas, il faut synhcroniser
l'ensemble de la boucle (test du nombre d'élements de la queue et
Dequeue). Ca donne quelque chose du genre :
Dim continue_loop as bool = true;
Do While continue_loop
SyncLock Me 'je syncrhonise *toute* l'operation de Dequeue
continue_loop = Users.Count > 0
If continue_loop
Result = Users.Dequeue
Interlocked.Increment(Count)
End If
End SyncLock
' faire le traitement sur Result si continue_loop est vrai.
' Remarques qu'on peut faire ce traitement sans tenir le lock qui ne
' sert qu'à protéger la boucle.
Loop
ThreadEvent.Set()
Désolé pour les fautes de syntaxe (VB n'est vraiement pas ma tasse de
thé), mais ca te donne une idée.
Do While Users.Count > 0 Result = Users.Dequeue Interlocked.Increment(Count)
</snip>
Loop
Ceci n'est pas thread-safe du tout, même si Users est une liste "Synchronized". Cette idée de collections "Synchronized" est de toute façon foireuse (AMHA, c'est une mauvaise idée d'avoir introduit çà dans le framework au départ, ca implique trop de dangers que les gens se croient synchronisés alors qu'il ne le sont pas du tout). En effet, quasiement tout le temps, ce que tu veux rendre atomique, c'est un ensemble de plusieurs opérations sur le conteneur, et pas simplement une opération. Dans ton cas, il faut synhcroniser l'ensemble de la boucle (test du nombre d'élements de la queue et Dequeue). Ca donne quelque chose du genre :
Dim continue_loop as bool = true; Do While continue_loop SyncLock Me 'je syncrhonise *toute* l'operation de Dequeue continue_loop = Users.Count > 0 If continue_loop Result = Users.Dequeue Interlocked.Increment(Count) End If End SyncLock
' faire le traitement sur Result si continue_loop est vrai. ' Remarques qu'on peut faire ce traitement sans tenir le lock qui ne ' sert qu'à protéger la boucle.
Loop ThreadEvent.Set()
Désolé pour les fautes de syntaxe (VB n'est vraiement pas ma tasse de thé), mais ca te donne une idée.
Arnaud MVP - VC
Mikado
Merci Arnaud,
Pas grave pour la syntaxe :)
Tu peux m'expliquer pourquoi l'idée de collections "Sychronized" est foireuse ?
Bon sinon pour le code ça fonctionne toujours pas. InvokeMember, c'est pas une méthode asynchrone ça ?
Jérôme
Merci Arnaud,
Pas grave pour la syntaxe :)
Tu peux m'expliquer pourquoi l'idée de collections "Sychronized" est
foireuse ?
Bon sinon pour le code ça fonctionne toujours pas. InvokeMember, c'est pas
une méthode asynchrone ça ?
Tu peux m'expliquer pourquoi l'idée de collections "Sychronized" est foireuse ?
Bon sinon pour le code ça fonctionne toujours pas. InvokeMember, c'est pas une méthode asynchrone ça ?
Jérôme
Arnaud Debaene
Mikado wrote:
Merci Arnaud,
Pas grave pour la syntaxe :)
Tu peux m'expliquer pourquoi l'idée de collections "Sychronized" est foireuse ?
Dans ton code original :
Do While Users.Count > 0 Result = Users.Dequeue Interlocked.Increment(Count)
Le fait que la liste soit Synchronized garantit que l'appel à Users.Count est atomique (aucun n'autre thread ne manipulera la liste pendant cet appel), et garantit que Users.Dequeue sera atomique (même garantie).
Par contre, rien ne garantit qu'un aute thread ne va pas manipuler la liste (ajouter/supprimer/modifier un élément) entre l'appel à Users.Count et l'appel à Users.Dequeue (sans parler de l'increment de Count qui fait aussi partie logiquement de "l'opération atomique" telle qu'elle est dans ton algorithme). C'est pour çà que dans mon code, je protège l'ensemble de ces 3 opérations par un lock De manière générale, toute opération non triviale sur une objet réclame plusieurs appels de méthodes, et donc la synchronisation est nécessairement à l'extérieur de l'objet manipulé.
Bon sinon pour le code ça fonctionne toujours pas. InvokeMember, c'est pas une méthode asynchrone ça ?
Je n'ai pas regardé le traitement de ton Result, je me suis concentré sur l'erreur de synchro dans la Queue. Commences par faire tourner ton traitement de compilation 1 seule fois, sans t'encombrer du threading pour valider ce que tu fais. Notamment, tu ne vérifies pas les erreurs éventuelles de CompileAssemblyFromSource, ni si Instance est bien créée.
En tout cas, InvokeMember n'est pas asynchrone (RTFM!). Par contre, vérifie les bidingFlags que tu utilises pour cet appel : sans tester, il me semble qu'il faut au moins BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance
Arnaud MVP - VC
Mikado wrote:
Merci Arnaud,
Pas grave pour la syntaxe :)
Tu peux m'expliquer pourquoi l'idée de collections "Sychronized" est
foireuse ?
Dans ton code original :
Do While Users.Count > 0
Result = Users.Dequeue
Interlocked.Increment(Count)
Le fait que la liste soit Synchronized garantit que l'appel à Users.Count
est atomique (aucun n'autre thread ne manipulera la liste pendant cet
appel), et garantit que Users.Dequeue sera atomique (même garantie).
Par contre, rien ne garantit qu'un aute thread ne va pas manipuler la liste
(ajouter/supprimer/modifier un élément) entre l'appel à Users.Count et
l'appel à Users.Dequeue (sans parler de l'increment de Count qui fait aussi
partie logiquement de "l'opération atomique" telle qu'elle est dans ton
algorithme). C'est pour çà que dans mon code, je protège l'ensemble de ces 3
opérations par un lock De manière générale, toute opération non triviale sur
une objet réclame plusieurs appels de méthodes, et donc la synchronisation
est nécessairement à l'extérieur de l'objet manipulé.
Bon sinon pour le code ça fonctionne toujours pas. InvokeMember,
c'est pas une méthode asynchrone ça ?
Je n'ai pas regardé le traitement de ton Result, je me suis concentré sur
l'erreur de synchro dans la Queue. Commences par faire tourner ton
traitement de compilation 1 seule fois, sans t'encombrer du threading pour
valider ce que tu fais. Notamment, tu ne vérifies pas les erreurs
éventuelles de CompileAssemblyFromSource, ni si Instance est bien créée.
En tout cas, InvokeMember n'est pas asynchrone (RTFM!). Par contre, vérifie
les bidingFlags que tu utilises pour cet appel : sans tester, il me semble
qu'il faut au moins BindingFlags.InvokeMethod | BindingFlags.Public |
BindingFlags.Instance
Tu peux m'expliquer pourquoi l'idée de collections "Sychronized" est foireuse ?
Dans ton code original :
Do While Users.Count > 0 Result = Users.Dequeue Interlocked.Increment(Count)
Le fait que la liste soit Synchronized garantit que l'appel à Users.Count est atomique (aucun n'autre thread ne manipulera la liste pendant cet appel), et garantit que Users.Dequeue sera atomique (même garantie).
Par contre, rien ne garantit qu'un aute thread ne va pas manipuler la liste (ajouter/supprimer/modifier un élément) entre l'appel à Users.Count et l'appel à Users.Dequeue (sans parler de l'increment de Count qui fait aussi partie logiquement de "l'opération atomique" telle qu'elle est dans ton algorithme). C'est pour çà que dans mon code, je protège l'ensemble de ces 3 opérations par un lock De manière générale, toute opération non triviale sur une objet réclame plusieurs appels de méthodes, et donc la synchronisation est nécessairement à l'extérieur de l'objet manipulé.
Bon sinon pour le code ça fonctionne toujours pas. InvokeMember, c'est pas une méthode asynchrone ça ?
Je n'ai pas regardé le traitement de ton Result, je me suis concentré sur l'erreur de synchro dans la Queue. Commences par faire tourner ton traitement de compilation 1 seule fois, sans t'encombrer du threading pour valider ce que tu fais. Notamment, tu ne vérifies pas les erreurs éventuelles de CompileAssemblyFromSource, ni si Instance est bien créée.
En tout cas, InvokeMember n'est pas asynchrone (RTFM!). Par contre, vérifie les bidingFlags que tu utilises pour cet appel : sans tester, il me semble qu'il faut au moins BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance