FromCurrentSynchronizationContext,我错过了什么

本文关键字:什么 错过了 FromCurrentSynchronizationContext | 更新日期: 2023-09-27 18:34:39

我目前正在处理一个从大型二进制文件中读取的应用程序,该文件包含数千个文件,每个文件都由应用程序中的其他类处理。此类返回对象或 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数据流的新手,所以我希望有人可以分享一些关于为什么在第二个代码片段中它有效而在第一个代码片段中它不起作用的启示。

亲切问候马蒂恩

FromCurrentSynchronizationContext,我错过了什么

您的 UI 被阻止的原因是您使用的是 FromCurrentSynchronizationContext 。它会导致代码在 UI 线程上运行,这意味着如果您正在执行一些长时间运行的操作(很可能是 GetFileFromLargeFile()(,它将冻结。

另一方面,您必须在 UI 线程上运行lblProgress.Text

我不确定您是否应该直接在此代码中设置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);