0

BackgroundWorker前景に進行状況を表示しながら、いくつかの処理ジョブを実行するためにを使用しています。これで、他のスレッドで使用されるプロパティにアクセスすることは想定されていないことがわかりました。これにより、あらゆる種類のエラーが発生するようです。しかし、次の例では、そのようなことはしていないと思いますが、メソッドBackgroundWorkerに到達すると魔法のように終了します。ShowDialog()

public class ProcessingWindow
{    
    List<ProcessingJob> m_processingJobs = new List<ProcessingJob>();
    private BackgroundWorker m_backGroundWorker = new BackgroundWorker();

    public ProcessingWindow() 
    { 
        InitializeComponent();

        /* Fill m_processingJobs with jobs */

        m_backGroundWorker.WorkerReportsProgress = true;
        m_backGroundWorker.WorkerSupportsCancellation = true;

        m_backGroundWorker.DoWork += m_backgroundWorker_DoWork;
        m_backGroundWorker.ProgressChanged += m_backgroundWorker_ProgressChanged;
        m_backGroundWorker.RunWorkerCompleted += m_backgroundWorker_RunWorkerCompleted;

        this.Loaded += ProcessingProgressWindow_Loaded;
    }

    void ProcessingWindow_Loaded(object sender, RoutedEventArgs e)
    {
        m_backGroundWorker.RunWorkerAsync();
    }

    private void m_backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        foreach (ProcessingJob job in m_processingJobs )
        {
             if (job.somethingIsWrong)
             {
                  SomethingIsWrongDialog dialog = new SomethingIsWrongDialog(); // <-- Here it crashes!
                  // This statement is therefore never reached:
                  dialog.showDialog();
             }
        }
     }

    private void m_backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.WriteLine("Finished!");
        this.Close();
    }
}

したがって、私のプログラムは のコンストラクターにヒットし、SomethingIsWrongDialogクラッシュする代わりにスレッドを停止し、m_backgroundWorker_RunWorkerCompleted何も起こらなかったかのようにメソッドを実行します。なぜこれが間違っているのですか?

4

3 に答える 3

3

多くの UI コンポーネントがこれを必要とするため、呼び出しスレッドは STA でなければなりません。

彼らは確かにそうします。クリップボード、ドラッグ アンド ドロップ、シェル ダイアログ (OpenFileDialog など) などの基本機能には、STA スレッドが必要です。

シングル スレッド アパートメントとして構成されたスレッドは、基本的にスレッドセーフでないコンポーネントにスレッド セーフ保証を提供できます。それらの多くは、特にユーザー インターフェイスで動作する種類のものです。STA スレッドが行うことで、ワーカー スレッドが行わないことの 1 つに、メッセージ ループのポンプがあります。WPF のディスパッチャ ループ。これにより、スレッドセーフな呼び出しを行う方法が提供され、その STA スレッドでメソッドを呼び出すことができます。Dispatcher.Begin/Invoke() メソッドによって WPF で公開されます。スレッドセーフではないコンポーネントは、その呼び出しを自動的に行う方法を知っています。これは、独自の WPF コードで Dispatcher.BeginInvoke() を使用して独自に記述する場合と同じです。COM コンポーネントを作成する場合、BeginInvoke() を記述する必要さえありません。COM が自動的に行います。

BackgroundWorker で使用されるスレッドプール スレッドのように、MTA に参加したスレッドは、この保証を提供できません。重要なディスパッチャ ループはありません。したがって、そのようなスレッドに表示する UI は誤動作するだけで、コピー/貼り付けなどの単純な操作は機能しなくなります。メッセージが示すように、多くの UI コンポーネントがこれを必要とします。

ディスパッチャ ループのコア属性は、生産者と消費者の問題に対するソリューションです。オペレーティング システムがプロデューサーであり、たとえばユーザーがマウスやキーボードを操作したときに非同期通知を生成します。そしてあなたのUIは消費者です。

于 2013-10-22T12:22:05.480 に答える
1

バックグラウンド ワーカーが UI スレッドではなく実行されています。あなたがしようとしているのは、バックグラウンドで実行してから、UI に戻って何かを表示することです。

それは間違っています。私にとって最も簡単なのは、IProgressNotifier オブジェクトをバックグラウンド ワーカーに渡すことです。public interface IProgressNotifier { bool ShowWarning(string text);}

次に、ViewModel (この backgrround ワーカーの実行を呼び出した場所) がそれを実装し、ディスパッチャーを介してこのウィンドウを表示する必要があります。

EDIT サンプルの変更:

interface IWorkNotifier
{
    void ShowError(string text);
}
public class ProcessingWindow: IWorkNotifier
   ...

public void ShowError(string text)
{
    Application.Current.Dispatcher.Invoke((Action)(() => DoShowError(text)));
}
private void DoShowError(string text)
{
    SomethingIsWrongDialog dialog = new SomethingIsWrongDialog();
    dialog.ShowDialog();
}
void ProcessingProgressWindow_Loaded(object sender, RoutedEventArgs e)
    {
        m_backGroundWorker.RunWorkerAsync(this);
    }
private void m_backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    var arg = (IWorkNotifier)e.Argument;
    foreach (ProcessingJob job in m_processingJobs)
    {
        if (job.somethingIsWrong)
        {
            arg.ShowError("shit happened");
        }
    }
}
于 2013-10-22T11:32:28.207 に答える