gpt4 book ai didi

c# - 任务并行库代码在 Windows 窗体应用程序中卡住 - 作为 Windows 控制台应用程序正常工作

转载 作者:太空狗 更新时间:2023-10-29 22:10:56 24 4
gpt4 key购买 nike

这个问题是我之前提出的一个问题的后续问题:

How to Perform Multiple "Pings" in Parallel using C#

我能够让接受的答案(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;
}

}

}

有人知道为什么会结冰吗?

最佳答案

它卡住是因为 WaitAll 等待所有任务,而您在 UI 线程中,因此阻塞了 UI 线程。阻塞 UI 线程会卡住您的应用程序。

因为您使用的是 C# 5.0,所以您想做的是 await Task.WhenAll(...)。 (您还需要在其定义中将该事件处理程序标记为 async。)您不需要更改代码的任何其他方面。那会很好用。

await 实际上不会在任务中“等待”。它会做的是,当它遇到 await 时,它将继续连接到您正在 await 执行的任务(在本例中为 when all),并且在该继续中它将运行该方法的其余部分。然后,在连接该延续之后,它将结束该方法并返回给调用者。这意味着 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;
}

这是另一个基于 this suggestion 的选项在 svick 的评论中。

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

现在我们有了 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());

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

关于c# - 任务并行库代码在 Windows 窗体应用程序中卡住 - 作为 Windows 控制台应用程序正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13406901/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com