You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin中Task.WhenAll执行批量Ping时偶发无异常崩溃问题求助

问题:Xamarin应用网段Ping扫描偶发崩溃(SIGBUS)

我正在开发一款基于Xamarin的应用,用于通过SMB2查找已连接PC的IP地址。通过await Dns.GetHostAddressesAsync(Dns.GetHostName())获取本机IP的前三段,随后Ping该网段内0-255的所有IP(如xxx.xxx.xxx.0到xxx.xxx.xxx.255)检测响应。

为每个IP创建独立Task执行Ping(共255个),调用Task.WhenAll(tasks)时偶发崩溃,每3-5次运行触发一次,且无.NET层面的异常信息抛出。

相关代码

PingAsyncCustom方法

async Task PingAsyncCustom (List<String> testIPs)
{
        if (Battery.PowerSource == BatteryPowerSource.Usb)
        {
            List<Task> tasks = new List<Task>();
            
            foreach (string ips in testIPs) 
            {
                using (Ping ping = new Ping())
                {
                    var taskPing = InitiatePing(ips, ping);
                    tasks.Add(taskPing);
                }
            }
            try
            {
                await Task.WhenAll(tasks);
            }
            catch (Exception e)
            {
                Crashes.TrackError(e);
                Analytics.TrackEvent("WhenAll Crash" + e.ToString());
            }
            Report("Waiting Finished");
            tasks.RemoveAll(x => x.IsCompleted);
            Report("Ping Async Finished");
        }
        else
        {
            Report("Disconected during Ping Sweep");
            pIps.Clear();
        }
    
}

InitiatePing方法

private async Task InitiatePing (String ip, Ping ping)
{
    var tcs = new TaskCompletionSource<PingReply>();
    var options = new PingOptions(64,true); 
    string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    byte[] buffer = Encoding.ASCII.GetBytes(data);
    try
    {
        Report("InitiatePing: " + ip);
        var reply = await ping.SendPingAsync(ip, 2000, buffer, options);
        if (reply.Status == IPStatus.Success)
        {
            lock (lockObj)
            {
                pIps.Add(reply);
            }
        }
    }
    catch(PingException e)
    {
        Report("PingException: " + e.ToString());
    }
}

运行时错误信息

Thread started: <Thread Pool> #99
Thread started: <Thread Pool> #100
Thread started: <Thread Pool> #101
Thread started: <Thread Pool> #102
Thread started: <Thread Pool> #103
Thread started: <Thread Pool> #104
Thread started: <Thread Pool> #105
Thread started: <Thread Pool> #106
Thread started: <Thread Pool> #107
[libc] Fatal signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr

问题原因及解决方案

核心原因

  1. Ping对象提前释放:PingAsyncCustom的foreach循环中,using会在每次循环结束时立即释放Ping对象,但异步的InitiatePing仍在使用该对象,导致访问已释放的非托管资源,触发底层内存访问错误(SIGBUS)。
  2. 线程池过载:一次性启动255个Ping任务,瞬间耗尽线程池资源,引发系统层面的内存异常。
  3. 底层异常未被捕获:Xamarin的Ping类依赖非托管资源,这类底层错误无法被.NET的try-catch捕获。

解决方案

1. 修正Ping对象生命周期

将Ping对象的创建和释放移到InitiatePing内部,确保异步操作完成后再释放资源:

private async Task InitiatePing(string ip)
{
    var options = new PingOptions(64, true);
    string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    byte[] buffer = Encoding.ASCII.GetBytes(data);
    
    try
    {
        Report("InitiatePing: " + ip);
        using (var ping = new Ping())
        {
            var reply = await ping.SendPingAsync(ip, 2000, buffer, options);
            if (reply.Status == IPStatus.Success)
            {
                lock (lockObj)
                {
                    pIps.Add(reply);
                }
            }
        }
    }
    catch (PingException e)
    {
        Report("PingException: " + e.ToString());
    }
    catch (Exception e)
    {
        Report("Unexpected ping error: " + e.ToString());
        Crashes.TrackError(e);
    }
}

2. 限制并发任务数量

用SemaphoreSlim控制并发数(建议10-20个),避免线程池过载:

async Task PingAsyncCustom(List<string> testIPs)
{
    if (Battery.PowerSource == BatteryPowerSource.Usb)
    {
        var semaphore = new SemaphoreSlim(15); // 限制15个并发任务
        var tasks = new List<Task>();
        
        foreach (var ip in testIPs)
        {
            tasks.Add(Task.Run(async () =>
            {
                await semaphore.WaitAsync();
                try
                {
                    await InitiatePing(ip);
                }
                finally
                {
                    semaphore.Release();
                }
            }));
        }
        
        try
        {
            await Task.WhenAll(tasks);
        }
        catch (Exception e)
        {
            Crashes.TrackError(e);
            Analytics.TrackEvent("WhenAll Crash: " + e.ToString());
        }
        
        Report("Ping Async Finished");
    }
    else
    {
        Report("Disconnected during Ping Sweep");
        pIps.Clear();
    }
}

3. 更优替代方案:ARP扫描

相比Ping扫描,ARP扫描更高效(无需等待ICMP响应),且不会触发大量线程创建。在Xamarin中可通过DependencyService调用Android/iOS原生ARP接口,直接获取局域网内活跃设备的IP和MAC地址,从根源避免Ping扫描的资源问题。

内容的提问来源于stack exchange,提问作者Ekcior

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 10:01:20