Control.Invoke()vs IProgress / ReportProgress

时间:2015-07-11 20:49:10

标签: c# multithreading winforms asynchronous

可以使用IProgressBackgroundWorker.ReportProgress从异步任务传递信息:

class Approach1 : UserControl
{
    enum Stage { INIT, STATUS, DATA, TIME, ... }
    struct ProgressObject
    {
         int StatusCode;
         int SecondsRemaining;
         IList<int> Data;
         Stage CurrentStage;
    }

    TextBox Status;

    async Task DoWork1(IProgress<ProgressObject> progress) 
    {
         await Task.Run( () =>
         {
             progress.Report(new ProgressObject(0, 0, null, Stage.INIT));
             int code = DoSomething();
             progress.Report(new ProgressObject(code, 0, null, Stage.STATUS));
             IList<int> Data = ...;
             progress.Report(new ProgressObject(0, 0, Data, Stage.DATA));
             int Seconds = ...;
             progress.Report(new ProgressObject(0, time, null, Stage.TIME));
         });
    }

    void ReportProgress(ProgressObject progress)
    {
        switch (progress.CurrentStage)
        {
            case Stage.CODE:
                Status.Text = DecodeStatus(progress.StatusCode);
                break;
            // And so forth...
        }
    }

    async void Caller1(object sender, EventArgs e)
    {
        var progress = new Progress<ProgressObject>(ReportProgress);
        await DoWork2(progress);
    }
}

但是,这也可以通过将委托传递给UI对象的BeginInvoke方法(Invoke,如果我们想要阻止)来完成:

class Approach2 : UserControl
{
    Textbox Status;

    int StatusCode
    {
        set
        {
            BeginInvoke(new Action( () => Status.Text = DecodeStatus(value));
        }
    }

    // Imagine several other properties/methods like the above:
    int SecondsRemaining;
    void UpdateData(IList<int> data);

    async Task DoWork2()
    {
        await Task.Run( () =>
        {
            StatusCode = DoSomething();
            UpdateData(...);
            SecondsRemaining = ...;
        });
    }

    async void Caller2(object sender, EventArgs e)
    {
        await DoWork1();
    }
}        

专用进度报告机制是否优先于Invoke?如果,为什么呢?是否有任何可能的问题&#39;两种方法都产生了什么?

恕我直言,与Invoke接受具有多个字段的进度结构相比,ReportProgress方式更简单/需要的代码更少,特别是如果在任务的多个阶段报告进度并且因此,报告方法需要分支到给定阶段的适当报告。

1 个答案:

答案 0 :(得分:2)

你应该从你的努力中得到一个提示,使Approach2实际编译。花了一段时间,没有?我看到你反复编辑代码片段了。你得到的另一个提示是,到达那里的唯一方法是从UserControl派生你的类。

Begin / Invoke()存在哪个问题,它只能在您有权访问Control对象时才能工作。库中的代码经常(并且应该)不知道UI的样子。它可能甚至没有在Winforms中实现,例如可以在WPF或Universal应用程序中使用。

进度和LT;&GT;它也适用于那些GUI类库,它使用更通用的方式来正确同步。由SynchronizationContext.Current属性提供,它依赖于GUI类库来安装提供程序。 Winforms安装的那个,WindowsFormsSynchronizationContext,在其Post()方法中自动调用BeginInvoke(),在其Send()方法中调用Invoke()。也是使async / await代码独立于UI实现的机制。

Progress&lt;&gt;有一个缺点,它可能完全无法以非常难以诊断的方式完成工作。对象必须由在应用程序的UI线程上运行的代码创建。如果不是,那么SynchronizationContext.Current没有值,并且ProgressChanged事件将在任意线程池线程上触发。 Kaboom如果您尝试使用事件处理程序更新UI,则您不会知道原因,因为异常发生在远离错误的代码中。

但是可以肯定的是,如果您将类硬编码为从System.Windows.Forms.UserControl派生,那么您几乎没有使用Progress&lt;&gt ;.除了感觉良好的感觉,当你将它移植到另一个GUI类库时,你将会做更少的工作。