2016-12-07 7 views
6

これは私の他の質問How to cancel background printingに関連しています。CancellationTokenSourceを使って別のスレッドのダイアログを閉じるには?

私はCancellationTokenSourceモデルをよりよく理解しようとしています。スレッド境界をまたいで使用する方法を理解しています。

私はコードが背後ん(UIスレッド上の)メインウィンドウがあります。それが閉じているときに正しくCloseWindowコードを呼び出す

public MainWindow() 
     { 
      InitializeComponent(); 

      Loaded += (s, e) => { 
       DataContext = new MainWindowViewModel(); 
       Closing += ((MainWindowViewModel)DataContext).MainWindow_Closing; 

      }; 
     } 

を:

private void CloseWindow(IClosable window) 
     { 
      if (window != null) 
      { 
       windowClosingCTS.Cancel(); 
       window.Close(); 
      } 
     } 

の選択にはメニュー項目、バックグラウンドスレッド上に第2のウィンドウが作成されます。

In the MainWi ndowViewModel(UIスレッド上では)、私が入れ:

public CancellationTokenSource windowClosingCTS { get; set; } 

のコンストラクタで:

// Constructor 
    public MainMenu() 
    { 
     readers = new List<Reader>(); 
     CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow); 
     windowClosingCTS = new CancellationTokenSource(); 
    } 

今私の問題。 UIスレッドでMainWindowを閉じると、windowClosingCTS.Cancel()によって、ctで登録されたデリゲートへの即時呼び出しが発生します。つまり、previewWindow.Close()が呼び出されます。異なる スレッドがそれを所有しているので、呼び出し元のスレッドがこのオブジェクトにアクセスすることはできません 『

をそう:"

(!Windowsの= NULL)とした場合、』これは今すぐに戻って投げます。私が間違って何をやっている?

+0

:あなたはあなたのソリューションで滞在したい場合は、バックグラウンドスレッドからキャンセルをトリガしたい場合

または、あなたはあなたのウィンドウがで開かれているスレッドに、あなたの近くに操作をマーシャリングする必要がありますあなたはWPFを使用していますか? –

+0

このプロセスでは、マルチスレッドを使用しないでください。あなたはCPUに束縛されていません。非同期シングルスレッドを使用することに固執する。 – Aron

+0

@ d.moncadaはいMVVM。 –

答えて

4

を呼び出します。取り消しをトリガーすると、プレビューが実行されているスレッドではなく、そのスレッドで取り消しトークンの登録済みアクションを実行します。

これらの場合のゴールドスタンダードはには2つのUIスレッドを使用しません。これは通常問題を引き起こし、それらを処理するために必要な作業は通常それに値するものではありません。私は仮定してい

Action closeAction =() => previewWindow.Close(); 
previewWindow.Dispatcher.Invoke(closeAction); 
+0

これは完全に機能しました。だから私は正しくcancelationtokenを使用していると思います。 1つのスレッドだけを使用するように(複数の人々によって)私に指摘されているので、ブロックせずにこれを行う方法はわかりません。私の固定文書の作成では、ネットワークサーバー上の数千のレコードの多くのフレームワーク要素の測定とレイアウトが使用されます。フレームワーク要素にはSTAスレッドが必要です。 TASKはMTAスレッドを使用しているため、2つは混在しません(なぜ私は他の質問にもこのコードを使用しました)。私は他の人がこの問題にどのように近づいているのか不思議です。ありがとう。 Dispatcher.Invokeが私の質問に答えました。 –

+0

@AlanWayneタスクはスレッドとは関係ありません。タスクは、MTAスレッドを使用しないことが最も確実です。 'Dispatcher.Invoke'はこれを.net 4.5でやり直すための「正しい」方法です。しかし、我々は.net 4.6.2上にいる、時が変わった。 – Aron

+2

@ AlanWayne:解決策は、通常、2番目のUIスレッドではなく、非UIワーカースレッドを開始することです。通常、UI要素からのデータへのアクセスはパフォーマンス上重要ではありません。そのためには、まずUI要素から同期的にデータを収集し、ワーカー・タスクを開始し、その結果をUIスレッドにマーシャリングして情報を表示します。また、あなたの計算方法でasync/awaitを使用することもできます。非同期/待機(他の質問のXPS書き込みメソッドのような)メソッドをサポートしていない場合は、 'TaskCompletionSource'を使用します(私の答えを確認してください)。 – Sefe

3

あなたのコードに問題がSELEで

ですメニュー項目のction、第二のウィンドウが バックグラウンドスレッドで作成されます。

// Print Preview 
public static void PrintPreview(FixedDocument fixeddocument, CancellationToken ct) 
{ 
    // Was cancellation already requested? 
    if (ct.IsCancellationRequested) 
      ct.ThrowIfCancellationRequested(); 

      ............................... 

     // Use my custom document viewer (the print button is removed). 
     var previewWindow = new PrintPreview(fixedDocumentSequence); 

     //Register the cancellation procedure with the cancellation token 
     ct.Register(() => 
       previewWindow.Close() 
     ); 

     previewWindow.ShowDialog(); 

    } 
} 

そして、私は推測

Task.Run(() => PrintPreview(foo, cancel)); 

なるための正しい解決策は、単一のスレッド上のすべてを行うことです。

public static Task<bool> PrintPreview(FixedDocument fixeddocument, CancellationToken ct) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 
    // Was cancellation already requested? 
    if (ct.IsCancellationRequested) 
      tcs.SetResult(false); 
    else 
    { 
     // Use my custom document viewer (the print button is removed). 
     var previewWindow = new PrintPreview(fixedDocumentSequence); 

     //Register the cancellation procedure with the cancellation token 
     ct.Register(() => previewWindow.Close()); 



     previewWindow.Closed += (o, e) => 
     { 
      var result = previewWindow.DialogResult; 
      if (result.HasValue) 
       tcs.SetResult(result.Value); 
      else 
       tcs.SetResult(false); 
     } 
     previewWindow.Show(); 
    } 

    return tcs.Task; 
} 

その後、あなたの問題はあなたのプレビューウィンドウは、別のスレッド上で実行されるということです

var shouldPrint = await PrintPreview(foo, cancel); 
if (shouldPrint) 
    await PrintAsync(foo); 
+0

これがどのように非同期に実行されるのかわかりません。また、TCSがDialogResultに1回、boolに別のTCSがある場合には、結果を設定するときに問題が発生します。 – Sefe

+0

@Aron私は最初、同じスレッド上ですべてを行いましたが、固定ドキュメントを作成するには、フレームワーク要素を作成してデータベースを大量に使用する必要があります。これを非同期的に行っても、UIスレッドが効果的にブロックされている間に数分かかることがありました。作成、印刷、printpreviewを別のスレッドに入れてみると、実際にはブロックせずにうまくいきます。試してキャンセルするまでです。 –

+0

@AlanWayneデータベースアクセスはUIスレッドをブロックしてはいけません。 asyncを使っていると仮定します。 DBアクセスはレイテンシ/帯域幅に制限があり、CPUに制限はありません。非同期データベース呼び出しを使用できるはずです。 – Aron

関連する問題