30

私が作成した1つのスレッドがソースから画像を継続的に取得し(実際にはデジタルカメラです)、GUIのパネル(panel.Image = img)に配置するC#デスクトップアプリケーションがあります(これは別のスレッドである必要があります)コントロールのコード ビハインドです。

アプリケーションは動作しますが、一部のマシンではランダムな時間間隔で次のエラーが発生します (予測不能)

************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere. 

次に、パネルが赤い十字、赤い X に変わります。これは、プロパティから編集できる無効な画像アイコンだと思います。アプリケーションは引き続き動作しますが、パネルは更新されません。

私が知る限り、このエラーはコントロールの onpaint イベントが原因で、画像に何か他のものを描画します。

私はそこでロックを使用しようとしましたが、運がありません:(

パネルに画像を配置する関数を呼び出す方法は次のとおりです。

if (this.ReceivedFrame != null)
{
    Delegate[] clients = this.ReceivedFrame.GetInvocationList();
    foreach (Delegate del in clients)
    {
        try
        {
            del.DynamicInvoke(new object[] { this, 
                new StreamEventArgs(frame)} );
        }
        catch { }
    }
}

これはデリゲートです:

public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
    public event ReceivedFrameEventHandler ReceivedFrame;

これは、制御分離コード内の関数がそれに登録する方法です。

Camera.ReceivedFrame += 
    new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);

私も試しました

del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });

それ以外の

del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });

しかし運がない

このエラーを修正する方法、または少なくとも何らかの方法でエラーをキャッチして、スレッドに画像をパネルに再度配置させる方法を知っている人はいますか?

4

4 に答える 4

20

これは、Gdi+ Image クラスがスレッドセーフではないためです。ただし、ペイントや画像サイズの取得など、画像にアクセスする必要があるたびにロックを使用することで、InvalidOperationException を回避できます。

Image DummyImage;

// Paint
lock (DummyImage)
    e.Graphics.DrawImage(DummyImage, 10, 10);

// Access Image properties
Size ImageSize;
lock (DummyImage)
    ImageSize = DummyImage.Size;

ところで、上記のパターンを使用する場合、呼び出しは必要ありません。

于 2009-06-29T20:29:11.090 に答える
5

同じエラーメッセージで同様の問題がありましたが、ビットマップをロックしても何も解決しませんでした。その後、静的ブラシを使用して形状を描いていることに気付きました。案の定、スレッド競合の原因はブラシでした。

var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
    e.Graphics.FillRectangle(brush, location);

これは私のケースでうまくいき、学んだ教訓:スレッドの競合が発生している時点で使用されているすべての参照型を確認してください。

于 2013-01-05T23:03:56.930 に答える
2

私には、同じ Camera オブジェクトが数回使用されているようです。

たとえば、受信したフレームごとに新しいバッファを使用してみてください。ピクチャ ボックスが新しいフレームを描画している間に、キャプチャ ライブラリがそのバッファを再び満たすように思えます。したがって、高速のマシンではこれは問題にならない可能性がありますが、低速のマシンでは問題になる可能性があります。

フレームを受信するたびに、次のフレームを受信するように要求し、その要求で新しいフレーム受信バッファーを設定する必要がありました。

それができない場合は、最初にカメラから受信したフレームを新しいバッファーにコピーし、そのバッファーをキューに追加するか、2 つの交互バッファーを使用してオーバーランを確認します。myOutPutPanel.BeginInvoke を使用して camera_ReceivedFrame メソッドを呼び出すか、スレッドを実行してキューをチェックし、新しいエントリがある場合は mnyOutPutPanel.BeginInvoke を呼び出してメソッドを呼び出し、新しいバッファをパネル上の画像として設定します。

さらに、バッファを受け取ったら、Panel Invoke Method を使用してイメージの設定を呼び出します (キャプチャ ライブラリのスレッドではなく、ウィンドウ スレッドで実行されることを保証します)。

以下の例は、任意のスレッド (キャプチャ ライブラリまたは別のスレッド) から呼び出すことができます。

void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
    if(myOutputPanel.InvokeRequired)
    {
        myOutPutPanel.BeginInvoke( 
            new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame), 
            sender, 
            e);
    }
    else
    {
        myOutPutPanel.Image = e.Image;
    }
}
于 2012-03-18T19:48:07.757 に答える
0

これはマルチスレッドの問題だと思いますWindowsのゴールデンルールを使用し、メインスレッドの使用パネルのパネルを更新します。呼び出しこれはクロススレッドの例外を克服する必要があります

于 2009-06-29T20:27:35.260 に答える