等待所有线程结束
Posted
技术标签:
【中文标题】等待所有线程结束【英文标题】:Waiting for all threads to end 【发布时间】:2021-11-03 09:32:30 【问题描述】:我有一些代码想用于网络上的设备发现。 它只是简单地 ping 所述网络上的任何 IP 地址,如果得到答案,则将地址添加到列表中。
代码如下:
static void Main(string[] args)
//do stuff to get subnetmask and local address
//succession of for loop to increment ip address
PingAsync(ip_address);
//here I display the list of addresses that answered the ping
//first Readline() is to manually wait for threads to end so the list isn't empty
Console.Readline()
Console.WriteLine("List of all devices found :\n");
ipList.ForEach(delegate (string str)
Console.WriteLine($"\t=> str");
);
Console.ReadLine();
private static void PingAsync(IPAddress address)
AutoResetEvent waiter = new AutoResetEvent(false);
Ping pingSender = new Ping();
//items are added to ipList in PingCompletedCallback()
pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 1200;
PingOptions options = new PingOptions(64, true);
pingSender.SendAsync(address, timeout, buffer, options, waiter);
在 PingCompletedCallback 方法中,我将项目添加到将显示在主列表中的列表
private static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
Program prog = new Program();
if (e.Cancelled)
Console.WriteLine("Ping failed !");
Console.WriteLine(e.Error.ToString());
((AutoResetEvent)e.UserState).Set();
if(e.Error != null)
Console.WriteLine("Ping failed !");
Console.WriteLine(e.Error.ToString());
((AutoResetEvent)e.UserState).Set();
PingReply reply = e.Reply;
if(reply.Status.ToString().Equals("Success", StringComparison.OrdinalIgnoreCase))
//If the ping succeeds add the address to a list
ipList.Add(reply.Address.ToString());
((AutoResetEvent)e.UserState).Set();
代码在发现设备方面运行良好,唯一的问题是地址列表。如果我不手动,它将显示为空。
根据这些帖子(1)(2) 和其他一些帖子,使用 Thread.join 或 Task.Waitall 是要走的路。但是,与他们不同的是,我不是自己创建线程,而是让 SendAsync() 创建自己的线程。
另外,我无法更改 PingAsync() 以使其等待 SendAsync(),因为它返回 void。
我想知道你会在打印/使用 ipList 之前等待线程结束。
【问题讨论】:
这里有很多问题。但是,您最好使用更现代的SendPingAsync
然后等待它。
【参考方案1】:
这段代码大部分可以替换为Ping.SendPingAsync:
static async Task Main(string[] args)
using var pingSender = new Ping();
var options = new PingOptions(64, true);
var reply=await pingSender.SendPingAsync(address,1200,options)
if(reply.Status == IPStatus.Success)
...
并发请求 - 每个请求一个发件人
SendPingAsync
不能多次调用以从单个发件人发送多个请求。这样做会引发 InvalidOperationException。
一种解决方案是为每个呼叫创建和使用单独的发件人:
async Task<Reply> PingAsync(IPAddress address)
using var pingSender = new Ping();
var options = new PingOptions(64, true);
var reply=await pingSender.SendPingAsync(address,1200,options)
结合 LINQ,可以同时启动多个 ping 并收集结果:
var addresses=new List<IPAddress>();
...
//Create the tasks with LINQ
var tasks=addresses.Select(address=>PingAsync(address))
.ToArray();
//And wait for all of them to complete
var replies=await Task.WhenAll(tasks);
//Get the successful pings:
var successes=replies.Where(reply=>reply.Status==IPStatus.Success)
.Select(reply=>reply.Address.ToString())
.ToArray();
第一个 LINQ 查询将为每个地址调用 PingAsync
,并返回每个调用返回的 Task<Reply>
对象:
var tasks=addresses.Select(address=>PingAsync(address))
.ToArray();
查询实际上是在调用ToArray()
时计算的。所有 ping 请求将同时开始。
下一个调用等待他们全部完成:
var replies=await Task.WhenAll(tasks);
最后,最后一个 LINQ 查询检索成功 ping 的 IP:
var successes=replies.Where(reply=>reply.Status==IPStatus.Success)
.Select(reply=>reply.Address.ToString())
.ToArray();
【讨论】:
感谢您的回答。但是,由于 pingSender.SendPingAsync(),第一个 LINQ 查询会引发 InvalidOperationException:“异步调用已在运行”。它说我应该等待进程结束或终止它,然后再开始另一个进程。 @FrougeurPro duh,这就是我不阅读小字的结果。仍然可以发出并发请求,但这需要使用多个PingSender
实例
@FrougeurPro 我修改了代码以对每个请求使用不同的发件人
现在完美运行,再次感谢您的工作。以上是关于等待所有线程结束的主要内容,如果未能解决你的问题,请参考以下文章