2

私は現在、数千のファイルを保持する大きなバイナリ ファイルから読み取るアプリケーションを使用しており、すべてのファイルはアプリケーション内の他のクラスによって処理されています。このクラスは、オブジェクトまたは null を返します。メインフォームの進捗状況を表示したいのですが、何らかの理由で理解できません。

int TotalFound = 0;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
BufferBlock<file> buffer = new BufferBlock<file>();
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       TotalFound++;
       buffer.post(file);
       lblProgress.Text = String.Format("{0}", TotalFound);
    }
}, options);

「TaskScheduler.FromCurrentSynchronizationContext」を使用しても、上記のコードはフォームをフリーズします。なぜですか? 以下のコードを使用すると、フォームが正常に更新されるため

DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       Task.Factory.StartNew(() => {
          TotalFound++;
          buffer.Post(file);
       }).ContinueWith(uiTask => {
          lblProgress.Text = String.Format("{0}", TotalFound);
       },CancellationToken.None, TaskContinuationOptions.None, uiScheduler);           
    }
});

私はこのTPLデータフロー全体に不慣れなので、2番目のコードスニペットでは機能し、最初のスニペットでは機能しない理由について誰かが光を共有できることを願っています.

敬具、マルティン

4

1 に答える 1

6

UIがブロックされている理由は、を使用しているためですFromCurrentSynchronizationContext。これにより、コードが UI スレッドで実行されます。つまり、実行時間の長いアクション (ほとんどの場合) を実行している場合はフリーズしますGetFileFromLargeFile()

一方、lblProgress.TextUI スレッドで実行する必要があります。

このコードで直接設定する必要があるかどうかはわかりませんがlblProgress.Text、結合が強すぎるように見えます。しかし、それを行いたい場合は、UI スレッドでその行だけを実行する必要があると思います。

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        await Task.Factory.StartNew(
            () => lblProgress.Text = String.Format("{0}", TotalFound),
            CancellationToken.None, TaskCreationOptions.None, uiScheduler);
    }
});

しかし、より良い解決策は、GetFileFromLargeFile()非同期にして、UI スレッドで長時間実行されるアクションを実行しないようにすることです (ConfigureAwait(false)それを支援できます)。ActionBlockこれを行うと、UI をフリーズすることなく、UI スレッドでのコードを実行できます。

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = await GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        lblProgress.Text = String.Format("{0}", TotalFound)
    }
}, options);
于 2012-05-20T13:12:52.187 に答える