1

通常、スレッド内のコントロールにアクセスすると、いくつかのクロススレッド例外が発生します。私のC#WinFormsアプリケーションには、その例外を引き起こさない画像ボックスとツールストリップラベルがあります。理由がわかりませんが、誰か説明してもらえますか?

ここにいくつかのコードの説明があります:

メインフォームには、ピクチャーボックスとツールストリップラベルがあります。また、コントロールや追加のソースコードがない別のフォームへの参照があります。そして、メインフォームには、スレッドで動作する別のオブジェクトがあります。このスレッドは3つの異なるイベントを発生させることができ、メインフォームはこれらの3つのイベントにサブスクライブされます。

  • Event1により、toolstriplabelが更新されます(スレッドからの情報を使用)。
  • Event2により、画像ボックスが更新されます(スレッドからの新しい画像で)。

Event1とEvent2は完全に正常に機能します。私はinvokeメソッドを使用せず、クロススレッド例外なしでTextプロパティとBackgroundImageプロパティを直接変更します。

  • Event3は問題を起こします。他の形式を表示することになっていますが、crosstherad例外が発生します。BeginInvokeを使用してフォームを表示した場合にのみ機能します。

何故ですか?

編集:

マルチスレッドはMJPEGStreamオブジェクトによって実行されます。そのMJPEGStreamオブジェクトのNewFrameメソッドをサブスクライブします。

public partial class Form1 : Form
{
    private CAM cam;

    private PeekWindow frmPeekWindow;

    public Form1()
    {
        InitializeComponent();

        cam = new CAM();
        cam.NewImageMessageEvent += new NewImageEventHandler(cam_NewImageMessageEvent);
        cam.DetectionEvent += new DetectionEventHandler(cam_DetectionEvent);
        cam.FpsChangedMessageEvent += new FpsChangedEventHandler(cam_FpsChangedMessageEvent);
        cam.DetectionThreshold = (float)this.numDetectionThreshold.Value;

        frmPeekWindow = new PeekWindow();

        // without the next two lines, frmPeekwindow.Show() won't work if called in an event
        frmPeekWindow.Show();
        frmPeekWindow.Hide();
    }

    void cam_FpsChangedMessageEvent(object sender, FpsChangedEventArgs e)
    {
        lblFPS.Text = string.Format("fps: {0:0.0}", e.FPS);
    }

    void cam_DetectionEvent(object sender, DetectionEventArgs e)
    {
        if (chkEnablePeakWindow.Checked)
        {
            if (frmPeekWindow.InvokeRequired)
            {
                frmPeekWindow.Invoke((MethodInvoker)delegate()
                {
                    frmPeekWindow.Show();
                    frmPeekWindow.setImage(e.Image);
                });
            }
            else
            {
                frmPeekWindow.Show();
                frmPeekWindow.setImage(e.Image);
            }
        }
    }

    void cam_NewImageMessageEvent(object sender, NewImageEventArgs e)
    {
        picStream.BackgroundImage = e.Image;
    }
}

そして、これがCAMクラスです。

class CAM
{
    private object lockScale = new object();

    private MJPEGStream stream;
    private Bitmap image;

    public event NewImageEventHandler NewImageMessageEvent;
    public event FpsChangedEventHandler FpsChangedMessageEvent;
    public event DetectionEventHandler DetectionEvent;

    // configure (login, pwd, source)
    public CAM()
    {
        this.stream = new MJPEGStream("...");
        this.stream.Login = "...";
        this.stream.Password = "...";
        this.stream.NewFrame += new NewFrameEventHandler(OnNewFrame)
    }

    private void OnNewFrame(object sender, NewFrameEventArgs ev)
    {
        try
        {
            FpsChangedMessageEvent(this, new FpsChangedEventArgs(10));

            // get image
            image = ev.Frame;
            NewImageMessageEvent(this, new NewImageEventArgs(new Bitmap(image)));

            DetectionEvent(this, new DetectionEventArgs(new Bitmap(image)));
        }
        catch (Exception ex)
        {
            Console.Out.WriteLine(ex.Message);
        }
    }
}
4

3 に答える 3

2

クロススレッド例外は発生しませんが、これが安全な操作であるとは限りません。コントロールが不安定になる可能性は常にあります。あなたはそれがいつ起こるかわからないだけです。

Microsoftからの次の説明を参照してください。 http://msdn.microsoft.com/en-us/library/ms171728.aspx

Windowsフォームコントロールへのアクセスは、本質的にスレッドセーフではありません。コントロールの状態を操作するスレッドが2つ以上ある場合は、コントロールを強制的に不整合な状態にすることができます。競合状態やデッドロックなど、その他のスレッド関連のバグが発生する可能性があります。コントロールへのアクセスがスレッドセーフな方法で実行されていることを確認することが重要です。

于 2013-01-29T11:27:19.867 に答える
0

必ずしもBeginInvoke/Invokeを呼び出す必要はありません。操作がフォアグラウンドスレッドで実行されている場合もあれば、バックグラウンドで実行されている場合もあります。

どこにでもあるマイクロソフトのサンプルごとに、BeginInvoke/Invokeを呼び出す必要があるかどうかを確認する必要があります。

private void SetTextStandardPattern()
{
    if (this.InvokeRequired)
    {
        this.Invoke(SetTextStandardPattern);
        return;
    }
    this.text = "New Text";
}

これがサンプルを持っている素晴らしいマイクロソフトの記事です:http://msdn.microsoft.com/en-us/library/ms171728(v = vs.80) .aspx

パターンを「回避」する方法に関する別の記事があります:http: //www.codeproject.com/Articles/37642/Avoiding-InvokeRequired

于 2013-01-29T19:59:20.453 に答える
0

私はこれらの3つの可能性を念頭に置いています:

  1. アクションはすでにGUIスレッドにディスパッチされています。
  2. 現在、アクションをディスパッチする必要はありません。
  3. アクションはどういうわけかGUIスレッドから実行されます。

おそらく3番です。

于 2013-01-29T11:14:33.013 に答える