等待所有线程结束

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&lt;Reply&gt; 对象:

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 我修改了代码以对每个请求使用不同的发件人 现在完美运行,再次感谢您的工作。

以上是关于等待所有线程结束的主要内容,如果未能解决你的问题,请参考以下文章

面试官:如何让主线程等待所有的子线程结束之后再执行?我懵了

java中等待所有线程都执行结束

主线程啥都没做,就会等待子线程结束。这是为啥?

c# 怎么等待线程池中所有线程都运行结束在运行主线程

如何实现线程互等,线程2等待线程1结束后才继续执行。(可设置标志位) 求源代码

c#多线程,怎么等待别的线程都结束?谢谢!!