任务并行库的代码冻结在Windows窗体应用程序 - 作为一个Windows控制台应用程序工作正常 [英] Task Parallel Library Code Freezes in a Windows Forms Application - Works fine as a Windows Console Application

查看:242
本文介绍了任务并行库的代码冻结在Windows窗体应用程序 - 作为一个Windows控制台应用程序工作正常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这问题是后续到以前的问题,我曾问:

This question is a follow-up to a previous question that I had asked:

如何执行多个"&的Ping QUOT;在并行使用C#

我能得到公认的答案(一个Windows控制台应用程序)的工作,但是当我试图运行的代码Windows窗体应用程序,下面的代码将冻结包含该行 Task.WaitAll(pingTasks.ToArray())。下面是我试图运行的代码:

I was able to get the accepted answer (a Windows console application) to work, but when I tried to run the code in a Windows forms application, the following code will freeze on the line containing Task.WaitAll(pingTasks.ToArray()). Here is the code that I am trying to run:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.NetworkInformation;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            List<String> addresses = new List<string>();

            for (Int32 i = 0; i < 10; ++i) addresses.Add("microsoft.com");

            List<Task<PingReply>> pingTasks = new List<Task<PingReply>>();
            foreach (var address in addresses)
            {
                pingTasks.Add(PingAsync(address));
            }

            //Wait for all the tasks to complete
            Task.WaitAll(pingTasks.ToArray());

            //Now you can iterate over your list of pingTasks
            foreach (var pingTask in pingTasks)
            {
                //pingTask.Result is whatever type T was declared in PingAsync
                textBox1.Text += Convert.ToString(pingTask.Result.RoundtripTime) + Environment.NewLine;

            }

        }

        private Task<PingReply> PingAsync(string address)
        {
            var tcs = new TaskCompletionSource<PingReply>();
            Ping ping = new Ping();
            ping.PingCompleted += (obj, sender) =>
            {
                tcs.SetResult(sender.Reply);
            };
            ping.SendAsync(address, new object());
            return tcs.Task;
        }

    }

}

有没有人有任何想法,为什么它冻结?

Does anyone have any ideas as to why it's freezing?

推荐答案

它的冻结,因为为WaitAll 等待所有的任务,你在UI线程,所以这是阻塞UI线程。阻塞UI线程冻结您的应用程序。

It's freezing because WaitAll waits on all of the tasks, and you're in the UI thread, so that's blocking the UI thread. Blocking the UI thread freezes your application.

你想做的事,因为你在C#5.0是什么,是等待Task.WhenAll(。 ..)来代替。 (您还需要以纪念它的定义,事件处理程序异步)。你不会需要更改代码的任何其他方面。这会工作得很好。

What you want to do, since you're in C# 5.0, is await Task.WhenAll(...) instead. (You'll also need to mark that event handler as async in it's definition.) You won't need to change any other aspects of the code. That will work just fine.

等待不会真正等待的任务。它的作用是,当它击中AWAIT,它将继续连线到等待 ING上(在这种情况下,当所有的),并在你的任务该延续它将运行的方法的其余部分。然后,连接最多那延续之后,将结束该方法,并返回到调用者。这意味着UI线程没有被阻塞,因为这个单击事件将结束的时候了。

await won't actually "wait" in the tasks. What it will do is, when it hits the await, it will wire up a continuation to the task you are awaiting on (in this case, the when all) and in that continuation it will run the remainder of the method. Then, after wiring up that continuation, it will end the method and return to the caller. This means that the UI thread isn't blocked, since this click event will end right away.

(根据要求)​​如果你想解决这个使用C#4.0那么我们会需要通过从头开始编写 WhenAll 开始,因为它是在5.0加入。以下是我刚刚刮起。它也许并不像库实现高效,但它应该工作

(Upon request) If you want to solve this using C# 4.0 then we'll need to start by writing WhenAll from scratch, since it was added in 5.0. Here is what I just whipped up. It's probably not quite as efficient as the library implementation, but it should work.

public static Task WhenAll(IEnumerable<Task> tasks)
{
    var tcs = new TaskCompletionSource<object>();
    List<Task> taskList = tasks.ToList();

    int remainingTasks = taskList.Count;

    foreach (Task t in taskList)
    {
        t.ContinueWith(_ =>
        {
            if (t.IsCanceled)
            {
                tcs.TrySetCanceled();
            }
            else if (t.IsFaulted)
            {
                tcs.TrySetException(t.Exception);
            }
            else //competed successfully
            {
                if (Interlocked.Decrement(ref remainingTasks) == 0)
                    tcs.TrySetResult(null);
            }
        });
    }

    return tcs.Task;
}

下面是一个基于的由svick评论这个建议

public static Task WhenAll(IEnumerable<Task> tasks)
{
    return Task.Factory.ContinueWhenAll(tasks.ToArray(), _ => { });
}

现在,我们有 WhenAll 我们只需要使用,以及延续,而不是的await 。取而代之的为WaitAll 您将使用:

Now that we have WhenAll we just need to use that, as well as continuations, instead of await. Instead of WaitAll you'll use:

MyClass.WhenAll(pingTasks)
    .ContinueWith(t =>
    {
        foreach (var pingTask in pingTasks)
        {
            //pingTask.Result is whatever type T was declared in PingAsync
            textBox1.Text += Convert.ToString(pingTask.Result.RoundtripTime) + Environment.NewLine;
        }
    }, CancellationToken.None,
    TaskContinuationOptions.None,
    //this is so that it runs in the UI thread, which we need
    TaskScheduler.FromCurrentSynchronizationContext());

现在你明白为什么5.0的选项是漂亮的,这是一个相当简单的用例了。

Now you see why the 5.0 option is prettier, and this is a reasonably simple use case too.

这篇关于任务并行库的代码冻结在Windows窗体应用程序 - 作为一个Windows控制台应用程序工作正常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆