任务并行库的代码冻结在Windows窗体应用程序 - 作为一个Windows控制台应用程序工作正常 [英] Task Parallel Library Code Freezes in a Windows Forms Application - Works fine as a Windows Console Application
问题描述
这问题是后续到以前的问题,我曾问:
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 await
ing 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屋!