从父表单中的TPL任务更新复杂子项

时间:2014-12-09 21:36:52

标签: c# winforms asynchronous task-parallel-library

我正在尝试使用可显示耗时任务进度的表单升级我的winforms应用程序,但无论我做什么,表单都无法正常工作。这就是我所拥有的:

  • 设定:

有一个表单(Form1),带有一个按钮和以下代码:

public partial class Form1 : Form
{
    CancellationTokenSource cToken = null;
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        cToken = new CancellationTokenSource();
        using (Form2 form = new Form2())
        {
            form.Show(); //Shows the 'progress' form
            int i = 0;
            Task.Factory.StartNew(() =>
            {
                while (cToken.Token.IsCancellationRequested == false && i <= 5) //Runs the task until it is cancelled or has reached its end
                {

                    form.UpdateProgress(i); //Updates the 'progress' form
                    Thread.Sleep(500); //Waits simulating time consuming activity
                    i++;
                }
            }, cToken.Token, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Wait();
        }
    }
}

然后在运行耗时的操作时,Form1会显示另一个表单(Form2):

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
        progressBar1.Step = 1;
        progressBar1.Maximum = 6;
    }

    public void UpdateProgress(int i)
    {
            progressBar1.PerformStep();
            label1.Text = string.Format("Step {0} of 6", i);
    }
}

此表单包含进度条,标签(显示百分比)和取消按钮。

  • 预期行为:

当一个耗时的操作正在运行时,将显示Form2(带有进度条的那个),并且每次调用UpdateProgress()时都需要更新进度条和标签文本。同时,这两个表单都不应该或似乎被阻止,以便用户可以随时取消耗时的任务。

  • 当前行为:

显示Form2,进度条正确更新,但不显示其他控件,并且两个表单都被阻止。

  • 其他信息:

我正在使用C#4.0,所以我不能使用Progress,我想避免使用BackgroundWorker或事件(不是我不喜欢它们,我只想这次使用TPL。)

  • 问题:

我是否可以使用TPL实现目标?我已经谷歌搜索和阅读MSDN文档好几天了,但到目前为止还没有找到一个有效的解决方案。我在这里遇到了很多关于stackoverflow的主题,但是他们主要展示了如何异步更新同一表单中的进度条,我可以使它工作,但它并不是我真正需要的。我很确定我错过了一些东西,但我无法弄清楚是什么。

2 个答案:

答案 0 :(得分:0)

线程末尾的.Wait()是导致同步行为的原因。

答案 1 :(得分:0)

代码使用TaskScheduler.FromCurrentSynchronizationContext()在UI线程上启动新的tash ,然后在其上调用.Wait()

您需要删除.Wait()TaskScheduler.FromCurrentSynchronizationContext()。在子表单UpdateProgress内,您应该进行相应的Invoke调用以更新进度条,或者在UI线程那里使用Task.StartNew 更新UI。

您还应该考虑将Microsoft.Bcl.Async包添加到项目中,这会为.NET 4.0项目添加async/awaitProgress<T>支持。这将允许您大大清理代码。