2

完了すると、winform を表示する別のタスクを続行するタスクがあります (winform は以前に UI スレッドで初期化されているため、ハンドルがあります)。

    private static Task RunningTask
    {
        get;
        set;
    }
    public static UpdaterTool.Forms.UpdateResult UpdateResultForm;

    private void DoWork()
    {
        UpdateResultForm = new Forms.UpdateResult(); 
        //the next line forces the creation of the handle - 
        //otherwise InvokeRequired will later on return false.
        var hackHandle = UpdateResultForm.Handle; 

        var ctx = TaskScheduler.FromCurrentSynchronizationContext();

        RunningTask = Task.Factory.StartNew(() => DownloadAndInstallFiles(), CancelTokenSource.Token)
            .ContinueWith(_ => WorkComplete(), CancelTokenSource.Token, TaskContinuationOptions.NotOnFaulted, ctx);
    }

    private void WorkComplete()
    {
       ShowResultForm();
    }

    private void ShowResultForm()
    {
        if (UpdateResultForm.InvokeRequired)
        {
            try
            {
                UpdateResultForm.Invoke(new MethodInvoker(ShowResultForm));
            }
            catch { }
            return;
         }
         UpdateResultForm.Show();

     }

問題は、使用する ContinueWith() のオーバーロードの組み合わせに関係なく、UpdateResultForm がまったく表示されない (継続が発生しないことを意味し、ワーカーが「実行中」でハングする) か、または継続中にハングすることです。ワーカースレッドが終了することを期待しているように、UI。FromCurrentSynchronizationContext() を使用して UI スレッドに表示しようとしたときに、なぜこれが発生するのかわかりません。

私の理解では、DoWork メソッド内で UI スレッドから開始します (そのため、そこでフォームを初期化します)。コードが Task.Factory.StartNew に入ると、作業スレッドに切り替わります。完了すると、以前に初期化されたフォームを UI スレッドに表示するだけの WorkComplete に進みます。

私は何が欠けていますか?ありがとう、

4

4 に答える 4

2

InvokeRequired の使用は強力なアンチパターンです。メソッドがどのスレッドを実行するかがわからないということは、それほど一般的ではありません。ここでも同様です。既に TaskScheduler.FromCurrentSynchronizationContext() を使用しているため、タスクが UI スレッドで実行されることがわかります。再確認しても無駄。これにより、フォーム ハンドルを早期に作成する必要もなくなります。

このような問題は、ワーカー スレッドから DoWork() を実行することによって発生する可能性があります。タスクからも呼び出した可能性があります。FromCurrentSynchronizationContext() が間違ったコンテキストを返すようにします。そして、メッセージループをポンプしないスレッドにフォームを表示します。それはドアネイルとして死んでしまいます。または、UI スレッドをブロックして、タスクが完了するのを待ちます。これは常にデッドロックを引き起こします。UI スレッドがアイドル状態になり、ShowResultForm() メソッドを実行できない限り、タスクは完了できません。

于 2012-07-21T15:09:30.210 に答える
1

SynchronizationContext を使用している場合は、InvokeRequired/Invoke は必要ありません。ここで表示されている問題が発生する唯一の方法は、DoWork が UI スレッド以外のスレッドで呼び出された場合です。

たとえば、提供されたコードを使用して独自の UpdateResult フォームを作成し、CancelTokenSource メンバーを追加してコンストラクターで初期化し、UI スレッドから DoWork を実行すると、正常に動作します。

于 2012-07-21T16:00:41.747 に答える
1

これに対する解決策は次のとおりだと思います: DoWork()UIスレッド以外のスレッドで呼び出されますか?

はいの場合は、UI スレッドでインスタンス化することを検討してください。UpdateResultFormそれが不可能な場合は、ContinueWith操作内のどこかでインスタンス化してください。

そうでない場合は、問題はないはずです。UpdateResultForm.Handle何らかのトラブルを引き起こした可能性があります。ただし、この場合、既に UI スレッドを使用しているため、このハンドルは必要ありません。したがって、Invoke が必要かどうかを確認する必要はありません。

どちらの場合でも、次のDoWorkようにメソッドを書き直してみることができます。

     private void DoWork()
            {
                var ctx = TaskScheduler.FromCurrentSynchronizationContext();

                RunningTask = Task.Factory
                    .StartNew(DownloadAndInstallFiles, CancelTokenSource.Token)
                    .ContinueWith(_ => ShowResultForm(), CancelTokenSource.Token, TaskContinuationOptions.NotOnFaulted, ctx);
            }

            private void ShowResultForm()
            {
                UpdateResultForm = new Forms.UpdateResult();
                // Some other code
                UpdateResultForm.Show();
            }

単にコールを通過させるだけなので、 EvenWorkCompleteは不要になりました。これがあなたを助けることを願っています。

于 2012-07-21T13:21:37.350 に答える
0

私はそれを解決しました。このスレッドの回答はすべて正しいです。ここでの問題は、Hans が示唆したように、FromCurrentSynchronizationContext() が予期される UI コンテキストではなく間違ったコンテキストを返すことでした。これは、UI スレッドからではなく、ワーカー スレッドから DoWork が呼び出されたために発生しました。

私の特定のケースでの解決策は、UI コンテキストから DoWork を呼び出すことでした。つまり、このブログ投稿で説明されているように、WindowsFormsSynchronizationContext オブジェクトを作成する必要がありました。

于 2012-07-22T12:24:00.813 に答える