简体   繁体   English

在WinForms上使用TPL进行并行编程

[英]Parallel Programming using TPL on WinForms

I trying to use TPL on WinForms .NET 4.0, I followed this steps (go to the end of article) that are for WPF and made some small changes so it could work on WinForms but it still doesn't work.. It should display result on label and richTextBox but it not... I think the parallel process work cause mouse start moving slow for a while when I click the button.. 我试图在WinForms .NET 4.0上使用TPL,我按照这些步骤(转到文章末尾)为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()
                                 )
                           );
                }
        );
}

Your code will not work because the thread UI to display result totally different with WPF. 您的代码将无法正常工作,因为显示结果的线程UI与WPF完全不同。 With WPF the thread UI is Dispatcher but on Windows Form is another one. 使用WPF,线程UI是Dispatcher,但在Windows Form上是另一个。

I have modified your code to help it work. 我修改了你的代码以帮助它工作。

    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()));
              });
    }

Leaving aside the whether it was good to do this way, from the learning angle and mentioned in comments topic "System.Windows.Threading.Dispatcher and WinForms?" 暂且不谈这样做是否合适,从学习角度并在评论主题“System.Windows.Threading.Dispatcher和WinForms?”中提到。 with somewhat confusing answer : 有点混乱的答案

"If you are sure to be in UI thread (eg. in an button.Click handler), Dispatcher.CurrentDispatcher gives you the UI thread dispatcher that you can use to dispatch from background thread to UI thread as usual" “如果您确定要在UI线程中(例如,在一个button.Click处理程序中),Dispatcher.CurrentDispatcher将为您提供UI线程调度程序,您可以像往常一样从后台线程调度到UI线程”

it is pertinent to mention that ( also provided my answer to mentioned above question): 有必要提一下( 也提供了我对上述问题的回答):

  • Task.Factory.StartNew() spawns executions on multiple threads different from the main UI or its child threads Task.Factory.StartNew()在与主UI或其子线程不同的多个线程上生成执行
  • it is possible to use Dispatcher on any thread 可以在任何线程上使用Dispatcher
  • WPF application OOTB (Out-of-thebox) provides System.Windows.Threading.Dispatcher DispatcherObject.Dispatcher of UI thread absent in Winfows forms WPF应用程序OOTB(Out-of-thebox)提供Winfows表单中缺少UI线程的System.Windows.Threading.Dispatcher DispatcherObject.Dispatcher
  • Used in question Dispatcher.CurrentDispatcher is getting the dispatchers on spawned by tasks non-ui threads 问题使用Dispatcher.CurrentDispatcher正在通过任务非ui线程生成调度程序

Anyway, from the didactic point of view of making minimum changes to the original WPF code , you should have caught and used UI dispatcher: 无论如何,从对原始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()
                                 )
                           );
                }
        );
} 

As described in the link below, the correct way would be to eliminate the use of Dispatcher class altogether.Instead, you should create a relevant instance of TaskScheduler and pass it to the Task methods. 如下面的链接所述,正确的方法是完全消除Dispatcher类的使用。相反,您应该创建TaskScheduler的相关实例并将其传递给Task方法。 http://blogs.msdn.com/b/csharpfaq/archive/2010/06/18/parallel-programming-task-schedulers-and-synchronization-context.aspx http://blogs.msdn.com/b/csharpfaq/archive/2010/06/18/parallel-programming-task-schedulers-and-synchronization-context.aspx

That is 那是

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

would become 会成为

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM