L’object est actuellement utilisé ailleurs

Je reçois cette erreur et il semble que c’est parce que le même object Bitmap est utilisé par différents threads. Cependant, j’utilise des serrures partout avec elle.

public class MySingleInstanceClass { private Object locker = new Object(); private Bitmap myImage = new Bitmap(100, 100); public Bitmap MyImage { get { lock (locker) return myImage; } private set { lock (locker) myImage = value; } } private void Refresh() { lock (locker) { var g = Graphics.FromImage(myImage); // do more processing } } } 

La classe MySingleInstanceClass n’aura qu’une seule instance. Les appels à MyImage et Refresh() peuvent provenir de différents threads. Autant que je sache, le code à l’intérieur du lock(locker) ne sera pas exécuté tant qu’il n’aura pas fini dans un autre thread, mais j’obtiens toujours l’erreur. Quelqu’un peut-il signaler une faille dans le code?

L’exception ressemble à ça:

Une exception de première chance du type ‘System.InvalidOperationException’ s’est produite dans System.Drawing.dll

Erreur: L’object est actuellement utilisé ailleurs.

sur System.Drawing.Graphics.FromImage (Image image)

at (pointe vers la ligne contenant var g = Graphics.FromImage (myImage);)

l’object locker n’est pas statique; ainsi chaque nouvelle instance crée son propre casier; vous devez créer un locker statique afin d’empêcher l’access d’autres threads si vous utilisez plusieurs objects.

 private static Object locker = new Object(); 

Pour un scénario d’object unique, l’utilisation d’une variable de niveau de classe non statique comme casier est appropriée. Si vous utilisez ce scénario, j’estime que la mise en oeuvre de Singleton pose quelques problèmes.

METTRE À JOUR:

 public sealed class MySingleInstanceClass { private static volatile MySingleInstanceClass instance; private static object syncRoot = new Object(); private Bitmap myImage; private MySingleInstanceClass() { myImage = new Bitmap(100, 100); } public static MySingleInstanceClass Instance { get { if (instance == null) { lock (syncRoot) { if (instance == null) instance = new MySingleInstanceClass(); } } return instance; } } public Bitmap MyImage { get { lock (syncRoot) return myImage; } private set { lock (syncRoot) myImage = value; } } public void Refresh() { lock (syncRoot) { var g = Graphics.FromImage(myImage); // do more processing } } } 

Peu importe que l’object verrouillé soit statique ou non. Le problème est que le lock(locker) la méthode getter se déverrouille dès que le bitmap est renvoyé. La référence renvoyée au bitmap n’est pas protégée par le verrou et peut être modifiée en même temps qu’un appel à Refresh .

Une solution possible serait de verrouiller le bitmap lui-même, mais cela peut créer des blocages s’il n’est pas fait avec soin.

Dans mon application, la meilleure solution était:

  • copier le répertoire avec les fichiers sur un autre, tmp. catalogue (avec nom de guide)
  • utiliser des fichiers tmp par utilisateur
  • supprimer le catalogue tmp avec les fichiers qu’il contient

Dans mon application, il y a:

  • chaque demande dure 1 min
  • le nombre maximum d’utilisateurs est de 120 (application intranet)
  • personne ne veut attendre 5-10 min pour que raport génère

Copier quelques fichiers ajoute environ 0,01-0,2 sek. pour chaque demande, il est préférable que le locking statique de toutes les applications et les utilisateurs ne doivent pas attendre 10 minutes pour que raport soit généré (10 utilisateurs cliquent sur le bouton Générer au même moment).

  private void DirectoryCopy(ssortingng sourceDirName, ssortingng destDirName, bool copySubDirs) { // Get the subdirectories for the specified directory. DirectoryInfo dir = new DirectoryInfo(sourceDirName); DirectoryInfo[] dirs = dir.GetDirectories(); if (!dir.Exists) { throw new DirectoryNotFoundException( "Source directory does not exist or could not be found: " + sourceDirName); } // If the destination directory doesn't exist, create it. if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); } // Get the files in the directory and copy them to the new location. FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { ssortingng temppath = Path.Combine(destDirName, file.Name); file.CopyTo(temppath, false); } // If copying subdirectories, copy them and their contents to new location. if (copySubDirs) { foreach (DirectoryInfo subdir in dirs) { ssortingng temppath = Path.Combine(destDirName, subdir.Name); DirectoryCopy(subdir.FullName, temppath, copySubDirs); } } } private void DeleteReportExecutionDirectory(ssortingng dirPath) { System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(dirPath); foreach (FileInfo file in downloadedMessageInfo.GetFiles()) { file.Delete(); } foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories()) { dir.Delete(true); } downloadedMessageInfo.Delete(); } 

vous pouvez cloner cette image avant de l’envoyer à la méthode

  Image newimg = (Image)img.Clone();