2013-02-02 119 views
5

このエラーが発生しています。同じビットマップオブジェクトが別のスレッドによってアクセスされているためです。しかし、私はどこでもロックを使用しています。オブジェクトは現在他の場所で使用されています

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

クラスMySingleInstanceClassにはインスタンスが1つだけあります。 MyImageRefresh()への通話は、異なるスレッドから来る可能性があります。私が理解する限り、lock(locker)のコードは別のスレッドで終了するまで実行されませんが、エラーはまだ発生します。誰でもコード内の欠陥を指摘できますか?

例外は、次のようになります。

A first chance exception of type 'System.InvalidOperationException' occurred in System.Drawing.dll

Error: Object is currently in use elsewhere.

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

at (points to the line containing var g = Graphics.FromImage(myImage);)

+0

ゲッター/セッター内のロックは無意味に見えます.... –

+0

MyImageのゲッター(および私設のセッター)をロックすることで、あなたは何を達成したと思いますか?私は大声で考えているので、私は解決策があると仮定しませんが、ロック動作がこのクラスの消費クラスにあった場合、それはより意味をなさないでしょうか? – bas

+1

@MitchWheatよく、私は、MyImageを呼び出すスレッドがRefresh()を呼び出すスレッドがコードブロックを終了するまで待っています。そうしないと、処理が完了する前にmyImageが返されます。私はそれが必要ではないと思いますか?どうして? –

答えて

9

lockerオブジェクトは静的ではありません。したがって、新しいインスタンスごとに独自のロッカーが作成されます。複数のオブジェクトを使用する場合は、他のスレッドからのアクセスを防ぐために、lockerを静的に作成する必要があります。

private static Object locker = new Object(); 

単一オブジェクトのシナリオでは、非静的クラスレベル変数をロッカーとして使用することが適切です。このシナリオを採用しているなら、シングルトンの実装にはいくつかの問題があると感じます。

UPDATE:

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

} 
+0

インスタンスが1つしかないので、ロッカーが1つ(クラスはsingletone) –

+0

です。あなたが提供したコードが質問と一致しませんか? – daryal

+0

@MitchWheat非静的オブジェクトを使用してインスタンスメンバーへのアクセスをロックできない理由を説明できますか? –

3

それはロックされているオブジェクトが静的であるかどうかは問題ではありません。問題は、ビットマップが返されるとすぐにgetterメソッド内のlock(locker)がロック解除されることです。返されたビットマップへの参照はロックによって保護されず、Refreshへの呼び出しが発生すると同時に変更することができます。

考えられる解決策の1つは、ビットマップ自体をロックすることですが、慎重に行わなければデッドロックが発生する可能性があります。

+0

ゲッターにビットマップのコピーを返すとどうなりますか? –

+0

'return myImage'を' return(Bitmap)myImage.Clone() 'に置き換えると、エラーも回避されます。しかし、MySingleInstanceClassを使用するコードを見ることなく、それが良い解決策になるかどうかは言うまでもありません。 – Dirk

+0

他の提供されているソリューションのMySingleInstanceClassに直接コメントを追加することはできないので、代わりにここに書きます。 2つのスレッドを想像してみてください。スレッド1は単に 'MySingleInstanceClass.Instance.Refresh()'を繰り返し呼び出します。 Thread 2は 'MySingleInstanceClass.Instance.MyImage'を呼び出し、返されたBitmapを変更します。これは、あなたの質問に私たちが示した例外を引き起こします。 – Dirk

0

あなたが前に、そのイメージのクローンを作成する最善の解決策だった私のアプリで方法

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

にそれを送ることができます、別のファイルで

  • コピーディレクトリTMPを。 (GUID名を持つ)カタログのユーザー
  • あたり
  • 利用TMPファイルが存在している私のアプリでは、それに

をファイルとTMPカタログを削除します

  • 各要求が1分長い
  • です
  • 最大ユーザー数は120人(イントラネットアプリケーション)
  • raportが生成されるまでに5-10分を待たない人

いくつかのファイルをコピーすると、約0,01-0,2セクを追加します。リクエストごとに、すべてのアプリとユーザーの静的ロックが、raportが生成するために10分(10人のユーザーが同じ瞬間にボタンを生成する)を待機するようにしてはいけません。

 private void DirectoryCopy(string sourceDirName, string 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) 
     { 
      string 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) 
      { 
       string temppath = Path.Combine(destDirName, subdir.Name); 
       DirectoryCopy(subdir.FullName, temppath, copySubDirs); 
      } 
     } 
    } 


     private void DeleteReportExecutionDirectory(string 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(); 
    } 
関連する問題