2010-01-26 3 views
4

J'essaie de créer un programme multithread qui prend un certain bitmap à partir d'une zone d'image où chaque thread analyse et modifie une partie de celui-ci, puis l'enregistre dans l'image. J'ai utilisé un verrou() pour les instructions qui traitent de l'objet bitmap partagé et de la zone d'image, mais pour une raison quelconque, je reçois toujours des erreurs "L'objet est actuellement utilisé ailleurs" toutes les 6 à 10 fois.Erreur de verrouillage de threads C# lors de la fusion d'images

 private Object locker = new Object(); 

    void doThread(Bitmap bmp2) //simplified - other references not important 
    { 
     //some code here 
     //.... 
     lock (locker) 
     { 
      Graphics gr = Graphics.FromImage(bmp2); //this is where i get the errors, they're related to bmp2 
      gr.DrawImage(bmp, new Rectangle(0, 0, 800, 600)); 
      gr.Dispose(); 

      pictureBox1.Image = bmp2; 
     } 
    } 

    void runThreads() 
    { 
     Bitmap bmp2 = new Bitmap(pictureBox1.Image); 

     Thread thread1 = new Thread(delegate() { doThread(bmp2); }); 
     Thread thread2 = new Thread(delegate() { doThread(bmp2); }); 
     Thread thread3 = new Thread(delegate() { doThread(bmp2); }); 
     Thread thread4 = new Thread(delegate() { doThread(bmp2); }); 

     thread1.Start(); 
     thread2.Start(); 
     thread3.Start(); 
     thread4.Start(); 
    } 

J'ai essayé de lire autant que je pouvais trouver sur la méthode de verrouillage(), mais il est encore un peu clair si je pourrais avoir abusé. Donc ma question est, pourquoi le verrou empêchant les threads d'exécuter les instructions? Ai-je abusé? Ou existe-t-il une solution de contournement que je pourrais utiliser?

Toute aide avec ceci est grandement appréciée.

+0

Qu'est-ce que "bmp" dans la ligne gr.DrawImage (bmp, new Rectangle (0, 0, 0, 0)); ? Est-ce que ça devrait être bmp2? –

+0

bmp est un bitmap déclaré à l'intérieur de la fonction doThread(), je copie le contenu de bmp en bmp2. BMP stocke la partie du bitmap avec laquelle le thread actuel a travaillé. – Bogdan

+0

bmp2 est-il utilisé à l'extérieur de la serrure? Je ne vois pas où l'erreur se produit autrement. –

Répondre

3

L'erreur se produit parce que votre thread UI utilise l'image (en particulier, en définissant pictureBox.Image = someImage provoquera la classe ImageAnimator du framework .NET regardez l'image, pour voir si elle devrait l'animer (pour les images animées .GIF, par exemple)

Pendant ce temps, votre thread d'arrière-plan change l'image, provoquant ainsi le code WinForms à lancer un "objet est actuellement en cours d'utilisation ailleurs "

Le code suivant fonctionne pour moi, ne se bloque jamais, peu importe le nombre de threads que je lui lance:

lock (locker) 
{ 
    using (Graphics gr = Graphics.FromImage(bmp2)) 
    { 
     gr.DrawImage(Resources.someImage, new Rectangle(0, 0, 800, 600)); 
     pictureBox1.Invoke(new Action(() => pictureBox1.Image = bmp2)); 
    } 
} 

Shoot, s'avère que cela n'a pas fonctionné non plus. Lancez suffisamment de threads, et ça va planter.

Je suppose que le problème est lié à Win32 peignant votre image bitmap alors que le thread d'arrière-plan dessine dessus. Une lecture de thread (UI), une écriture de thread (arrière-plan). Cela ne peut que conduire à des problèmes.

La meilleure solution pour les bogues multithread comme celui-ci est souvent d'arrêter de partager des données entre threads. Au lieu de cela, dupliquez les données et laissez chaque thread avoir sa propre copie locale.Voici un exemple:

lock (locker) 
{ 
    using (Graphics gr = Graphics.FromImage(bmp2)) 
    { 
     gr.DrawImage(Resources.someImage, new Rectangle(0, 0, 800, 600)); 
     var clone = bmp2.Clone() as Image; 
     pictureBox1.Invoke(new Action(() => pictureBox1.Image = clone)); 
    } 
} 
+0

mais pas pictureBox.Image est le problème. Le vrai problème est bmp2 qui est lu par les 4 threads et "dessiné" dans chaque thread. – Bogdan

+0

Non, pas vraiment. En raison du verrou et de l'appel Invoke, Bmp2 est lu par 1 thread à la fois: soit un thread d'arrière-plan, soit le thread d'interface utilisateur. Est-ce que le code ci-dessus fonctionne pour vous? Cela fonctionne parfaitement ici. –

+0

Ah, je viens de jeter plus de 5000 threads, et finalement il s'est écrasé. Tu as raison. Quelque chose d'autre se passe ici. Je verrai si je peux trouver quelque chose. –

5

La raison pour laquelle est la variable pictureBox1 a une affinité avec le thread graphique. Vous ne pouvez pas y accéder et changer sa valeur à partir d'un thread d'arrière-plan séparé. Pour changer la valeur, vous devez le faire à partir du thread auquel la variable est associée. Cela se fait généralement via .invoke

Essayez plutôt

pictureBox1.Invoke((MethodInvoker)(() => pictureBox1.Image = bmp2)); 

Même alors je pense que vous avez encore des questions parce que la BMP2 de valeur est utilisée de plusieurs threads. La variable pictureBox1 tente de rendre cette valeur sur le thread graphique pendant que le thread d'arrière-plan crée un objet graphique au-dessus.

1

Correction du problème d'interconnexion identifié par JaredPar.

Définissez ensuite pictureBox1.Image sur une copie de bmp2.

Image bmp2copy = bmp2.Clone(); 
pictureBox1.Invoke((MethodInvoker)(() => pictureBox1.Image = bmp2copy)); 

Espérons que cela fonctionne pour vous. Si ce n'est pas le cas, vous voudrez peut-être songer à mettre en place un projet barebones qui illustre le problème afin que les gens puissent l'exécuter et le faire avec le code. Threading issu peut être trop difficile à faire dans votre tête ...

+0

J'ai lu le post faux, il semble que cela fonctionne après tout! Merci beaucoup! – Bogdan