Hyd*_*per 5 c# ping winforms task-parallel-library .net-4.5
这个问题是我曾问过的上一个问题的后续问题:
我能够获得接受的答案(Windows控制台应用程序),但是当我尝试在Windows窗体应用程序中运行代码时,以下代码将冻结在包含的行上Task.WaitAll(pingTasks.ToArray()).这是我试图运行的代码:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
有没有人有任何想法,为什么它冻结?
Ser*_*rvy 16
它是冻结的,因为WaitAll等待所有任务,并且你在UI线程中,因此阻止了UI线程.阻止UI线程会冻结您的应用程序.
你要做什么,因为你在C#5.0,await Task.WhenAll(...)而不是.(您还需要async在其定义中标记该事件处理程序.)您无需更改代码的任何其他方面.这将工作得很好.
await实际上不会在任务中"等待".它会做什么,当它到达等待时,它将连接到你正在进行的任务await(在这种情况下,所有的时间),并在该延续中它将运行该方法的其余部分.然后,在连接该连续后,它将结束该方法并返回给调用者.这意味着UI线程未被阻止,因为此单击事件将立即结束.
(根据要求)如果你想用C#4.0解决这个问题,那么我们需要WhenAll从头开始编写,因为它是在5.0中添加的.这就是我刚刚掀起的东西.它可能不如库实现那么高效,但它应该工作.
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;
}
Run Code Online (Sandbox Code Playgroud)
以下是svick在评论中基于此建议的另一种选择.
public static Task WhenAll(IEnumerable<Task> tasks)
{
return Task.Factory.ContinueWhenAll(tasks.ToArray(), _ => { });
}
Run Code Online (Sandbox Code Playgroud)
现在WhenAll我们只需要使用它,以及延续,而不是await.而不是WaitAll你将使用:
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());
Run Code Online (Sandbox Code Playgroud)
现在你明白为什么5.0选项更漂亮了,这也是一个相当简单的用例.