C#中的任务的Windows窗体UI更新问题#

时间:2014-12-05 11:39:05

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

我们正在开发一个Windows应用程序,它可以满足工程计算的需要。因此,我们基本上试图将计算模块保持独立并在单独的工作线程中工作,并在方法签名中传递一个Action委托,该委托将被调用以报告UI中的计算进度。在UI中声明的委托处理程序将更新UI。我们发现,虽然计算中正在运行一个巨大的循环,但UI不会显示定期进度并且只显示最终结果。如果在计算循环中引入了1毫秒的线程休眠,则UI将正确更新。这不是预期的行为,因为我们使用单独的Task执行计算并使用BeginInvoke调用更新UI。

我创建了一个简单的应用程序来演示我们的方法和代码,以便更容易理解。显而易见的是,我们遗漏了一些非常简单的东西,但却无法确定它。将欣赏任何见解。

感谢阅读。

private void cmdStart_Click(object sender, EventArgs e)
    {
        txtResultDIsplay.Text = "";
        var maxIterations = long.Parse(txtIterationNo.Text.Trim());

        var ui = TaskScheduler.FromCurrentSynchronizationContext();
        Task<double> calculationTask = Task.Factory.StartNew<double>(() => SumRootN(maxIterations, UpdateProgress));
        var handleResultTask = calculationTask.ContinueWith((t) => DisplayResult(t),
                    CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ui);
    }

private void DisplayResult(Task<double> calculationTask)
        {
            txtResultDIsplay.Text = "Final Calculation Result : " + calculationTask.Result.ToString();
        }

private void UpdateProgress(string msg)
    {
        this.BeginInvoke((MethodInvoker)delegate
        {
            txtResultDIsplay.Text = msg;
        });
    }

public double SumRootN(long maxIterations, Action<string> progressUpdateDelegate)
    {
        int root = 20;
        double result = 0;
        for (long i = 1; i < maxIterations; i++)
        {
            Thread.Sleep(1);
            result += Math.Exp(Math.Log(i) / root);
            progressUpdateDelegate(result.ToString("0.00000"));
        }
        return result;
    }

1 个答案:

答案 0 :(得分:0)

您可能会在进度更新中充斥UI线程。您需要找到一种方法来防止发生大量更新。

我们可以使用任务解决问题!

Task progressTask = null;
private void UpdateProgress(string msg)
{
    //only schedule work if the task if not running
    if(progressTask == null || progressTask.IsCompleted) //updates will end if there is an exception!
    {
        //Create a task representing the update
        progressTask = Task.Factory.FromAsync<object>(BeginInvoke(new Action(() => txtResultDIsplay.Text = msg)), this.EndInvoke)
            .ContinueWith(() => System.Threading.Thread.Sleep(100)); //add a sleep on the end
    }
}

请注意,锁定不会在此处执行,因为如果已经发生更新,您可以跳过更新。