私は this SO question を通過しましたが、役に立ちませんでした。
ここでのケースは異なります。私はBackgroundworkersを使用しています。 1番目のbackgroundworkerがユーザーの画像入力で操作を開始し、firstbackgroundworker_runworkercompleted()内で他の3人のbackgroundworkerを呼び出して使用しています
algo1backgroundworker.RunWorkerAsync();
algo2backgroundworker.RunWorkerAsync();
algo3backgroundworker.RunWorkerAsync();
これはそれぞれのコードです:
algo1backgroundworker_DoWork()
{
Image img = this.picturebox.Image;
imgclone = img.clone();
//operate on imgclone and output it
}
algo2backgroundworker_DoWork()
{
Image img = this.picturebox.Image;
imgclone = img.clone();
//operate on imgclone and output it
}
同様の操作は、他のalgo * backgrougrondworker_doWork()でも実行されます。
「InvalidOperationException-オブジェクトは現在他の場所で使用されています」というメッセージが表示されることがあります。その非常に恣意的です。これは、algo1backgroundworker_DoWorkで取得することもあれば、algo2backgroundworker_DoWorkで取得することもあり、Application.Run(new myWindowsForm())で取得することもあります。
私は何が起こっているのか見当がつかない。
GDI +の内部には、2つのスレッドがビットマップに同時にアクセスするのを防ぐロックがあります。これはブロッキングの種類のロックではなく、「プログラマーが何か間違ったことをしたので、例外をスローします」の種類のロックです。すべてのスレッドでイメージのクローンを作成している(==ビットマップにアクセスしている)ため、スレッドが爆撃しています。 UIスレッドは、スレッドがクローンを作成すると同時にビットマップを描画しようとしている(==ビットマップにアクセスしている)ため、爆撃しています。
ビットマップへのアクセスを1つのスレッドのみに制限する必要があります。 BGWを開始する前に、UIスレッドでイメージのクローンを作成します。各BGWには、イメージの独自のコピーが必要です。 RunWorkerCompletedイベントでPBのImageプロパティを更新します。この方法では並行性がいくらか失われますが、それは避けられません。
したがって、BackgroundWorkersが同じWindowsフォームコンポーネントに同時にアクセスしようとしているようです。これは、失敗がランダムである理由を説明します。
lock
を使用して、これが発生しないことを確認する必要があります。おそらく次のようになります。
private object lockObject = new object();
algo1backgroundworker_DoWork()
{
Image imgclone;
lock (lockObject)
{
Image img = this.picturebox.Image;
imgclone = img.clone();
}
//operate on imgclone and output it
}
Imgcloneがこのメソッドに対してローカルであることを確認していることに注意してください。すべてのメソッドで共有したくないことは間違いありません!
一方、同じlockObjectインスタンスがすべてのメソッドで使用されます。 BackgroundWorkerメソッドがそのlock{}
セクションに入ると、そのポイントに到達した他のメソッドはブロックされます。したがって、ロックされたセクションのコードが高速であることを確認することが重要です。
処理された画像を「出力」するときは、UIに対してクロススレッド更新を行わないように注意してください。それを回避するためのきちんとした方法については、 この投稿 を確認してください。
Windowsフォームでは、単一のスレッドからコントロールにアクセスするだけでなく、そのスレッドをメインのアプリケーションスレッド、つまりコントロールを作成したスレッドにする必要があります。
これは、DoWorkでは(Control.Invokeを使用せずに)コントロールにアクセスしてはならないことを意味します。したがって、ここではRunWorkerAsyncを呼び出して、イメージクローンを渡します。 DoWorkイベントハンドラー内で、DoWorkEventArgs.Argumentからパラメーターを抽出できます。
ProgressChangedおよびRunWorkerCompletedイベントハンドラーのみがGUIと対話する必要があります。