上使用的WinForms TPL并行编程(Parallel Programming using TP

2019-08-22 23:25发布

我试图使用TPL上的WinForms .NET 4.0,我遵循这个步骤(去文章的末尾)是WPF和做一些小的修改,以便它可以在WinForms的工作,但它仍然无法正常工作。应该显示造成标签和RichTextBox中上,但它不是...我认为并行处理工作原因鼠标开始了一段缓慢移动,当我按一下按钮..

public static double SumRootN(int root)
{   double result = 0;
    for (int i = 1; i < 10000000; i++)
    {   result += Math.Exp(Math.Log(i) / root);}
    return result;
}
private void button1_Click(object sender, EventArgs e)
{   richTextBox1.Text = "";
    label1.Text = "Milliseconds: ";
    var watch = Stopwatch.StartNew();
    List<Task> tasks = new List<Task>();
    for (int i = 2; i < 20; i++)
    {   int j = i;
        var t = Task.Factory.StartNew
          (   () =>
                {   var result = SumRootN(j);
                    Dispatcher.CurrentDispatcher.BeginInvoke
                        (new Action
                             (   () => richTextBox1.Text += "root " + j.ToString() 
                                   + " " + result.ToString() + Environment.NewLine
                             )
                         , null
                        );
                 }
            );
        tasks.Add(t);
    }
    Task.Factory.ContinueWhenAll
         (  tasks.ToArray()
            , result =>
                {   var time = watch.ElapsedMilliseconds;
                    Dispatcher.CurrentDispatcher.BeginInvoke
                          (   new Action
                                (    () =>
                                      label1.Text += time.ToString()
                                 )
                           );
                }
        );
}

Answer 1:

您的代码将无法工作,因为线程UI显示结果和WPF完全不同。 随着WPF的UI线程调度是,但Windows窗体上是另一个。

我已修改了代码,以帮助它的工作。

    private void button1_Click(object sender, EventArgs e)
    {
        richTextBox1.Text = "";
        label1.Text = "Milliseconds: ";

        var watch = Stopwatch.StartNew();
        List<Task> tasks = new List<Task>();
        for (int i = 2; i < 20; i++)
        {
            int j = i;
            var t = Task.Factory.StartNew(() =>
            {
                var result = SumRootN(j);
                richTextBox1.Invoke(new Action(
                        () =>
                        richTextBox1.Text += "root " + j.ToString() + " " 
                              + result.ToString() + Environment.NewLine));
            });
            tasks.Add(t);
        }

        Task.Factory.ContinueWhenAll(tasks.ToArray(),
              result =>
              {
                  var time = watch.ElapsedMilliseconds;
                  label1.Invoke(new Action(() => label1.Text += time.ToString()));
              });
    }


Answer 2:

暂且不说它是否好做这种方式,从学习的角度和评论的话题提到的“System.Windows.Threading.Dispatcher和WinForms?” 与有些混乱的答案 :

“如果你一定要在UI线程(例如在button.Click处理),Dispatcher.CurrentDispatcher给你,你可以使用从后台线程调度到UI线程像往常一样在UI线程调度员”

它是有关一提的是( 也提供我的回答上述问题提到的):

  • Task.Factory.StartNew()派生从主UI不同的多个线程上执行或它的子线程
  • 它可以使用调度员在任何线程
  • WPF应用程序OOTB(外的thebox)提供System.Windows.Threading.Dispatcher DispatcherObject.Dispatcher在Winfows形式UI线程的缺席
  • 使用有问题Dispatcher.CurrentDispatcher越来越对任务的非UI线程催生了调度员

总之,从使原来的WPF代码变化最小的教学点,你应该抓住和用UI调度:

private void button1_Click(object sender, EventArgs e)
{   Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;//added **********
    richTextBox1.Text = "";
    label1.Text = "Milliseconds: ";
    var watch = Stopwatch.StartNew();
    List<Task> tasks = new List<Task>();
    for (int i = 2; i < 20; i++)
    {   int j = i;
        var t = Task.Factory.StartNew
          (   () =>
                {   var result = SumRootN(j);
      //Dispatcher.CurrentDispatcher.BeginInvoke//***changed to
                    dispatcherUI.BeginInvoke
                        (new Action
                             (   () => richTextBox1.Text += "root " + j.ToString() 
                                   + " " + result.ToString() + Environment.NewLine
                             )
                         , null
                        );
                 }
            );
        tasks.Add(t);
    }
    Task.Factory.ContinueWhenAll
         (  tasks.ToArray()
            , result =>
                {   var time = watch.ElapsedMilliseconds;
     //Dispatcher.CurrentDispatcher.BeginInvoke//**************changed to
                    dispatcherUI.BeginInvoke//added
                          (   new Action
                                (    () =>
                                      label1.Text += time.ToString()
                                 )
                           );
                }
        );
} 


Answer 3:

正如下面的链接描述中,正确的方法是消除使用Dispatcher类altogether.Instead的,你应该创建的TaskScheduler的相关实例,并把它传递给任务的方法。 http://blogs.msdn.com/b/csharpfaq/archive/2010/06/18/parallel-programming-task-schedulers-and-synchronization-context.aspx

那是

Task.Factory.ContinueWhenAll(tasks.ToArray(),
      result =>
      {
          var time = watch.ElapsedMilliseconds;
          this.Dispatcher.BeginInvoke(new Action(() =>
              label1.Content += time.ToString()));
      });

会成为

var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
    result =>
    {
        var time = watch.ElapsedMilliseconds;
        label1.Content += time.ToString();
    }, CancellationToken.None, TaskContinuationOptions.None, ui);


文章来源: Parallel Programming using TPL on WinForms