You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何循环C# ArcGIS异步地址搜索方法并实现任务同步触发

我来帮你搞定这个批量地址搜索的异步坑,结合你的房产距离计算场景一步步拆解解决:

核心问题拆解与解决方案

首先得纠正几个异步使用的误区,再给出适配你需求的代码实现:

1. 先把异步方法改对:告别async void

你之前用async void是事件处理的写法,普通异步任务必须用async Task/async Task<T>返回类型,这也是你改返回Task时出现无限等待的根源——大概率是在UI线程用了.Wait()/.Result导致死锁,改用await就能解决。

我把你的单地址搜索方法改成带返回值的异步方法,同时支持找到匹配后直接返回(停止当前任务):

// 改成async Task<MapPoint>,返回找到的坐标(没找到返回null)
public async Task<MapPoint> SearchSubjectAddressAsync(string sAddress, CancellationToken cancellationToken = default)
{
    var uri = new Uri("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer");
    var token = string.Empty;
    var locator = new OnlineLocatorTask(uri, token);
    
    // 传递取消令牌,支持后续主动终止(可选,但更健壮)
    var info = await locator.GetInfoAsync(cancellationToken);
    var singleAddressFieldName = info.SingleLineAddressField.FieldName;
    
    var address = new Dictionary<string, string>
    {
        { singleAddressFieldName, sAddress }
    };
    var candidateFields = new List<string> { "Score", "Addr_type", "Match_addr", "Side" };

    try
    {
        var results = await locator.GeocodeAsync(address, candidateFields, MyMapView.SpatialReference, cancellationToken);
        if (results.Count > 0)
        {
            var firstMatch = results[0];
            var matchLocation = firstMatch.Location as MapPoint;
            Console.WriteLine($"Found point: {matchLocation.ToString()}");
            MyMapView.SetView(matchLocation);
            return matchLocation; // 找到匹配直接返回,当前任务结束
        }
        else
        {
            Console.WriteLine($"No matches for address: {sAddress}");
            return null;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Failed to geocode {sAddress}: {ex.Message}");
        return null;
    }
}

2. 串行执行批量任务:前一个完成再启动下一个

要实现“同步循环执行异步方法”,直接用foreach+await就行,这样每个地址搜索任务会在前一个完全完成后才启动,完美符合你的需求:

// 批量处理入口方法
public async Task BatchProcessAddressesAsync(List<string> targetAddresses)
{
    // 存储地址和对应坐标,供后续计算使用
    var locationResults = new List<(string Address, MapPoint Location)>();

    // 串行遍历每个地址,await确保前一个任务完成再启动下一个
    foreach (var address in targetAddresses)
    {
        var location = await SearchSubjectAddressAsync(address);
        if (location != null)
        {
            locationResults.Add((address, location));
        }
    }

    // 所有搜索任务100%完成后,触发距离计算逻辑
    await CalculatePropertyDistancesAsync(locationResults);
}

// 你的距离计算方法示例
private async Task CalculatePropertyDistancesAsync(List<(string Address, MapPoint Location)> locations)
{
    if (locations.Count < 2)
    {
        Console.WriteLine("Not enough valid locations to calculate distances.");
        return;
    }

    // 假设以第一个地址为基准,计算和其他房产的距离
    var baseProperty = locations[0];
    foreach (var property in locations.Skip(1))
    {
        // 用ArcGIS的GeometryEngine计算距离(单位默认是米,可按需转换)
        var distance = GeometryEngine.Distance(baseProperty.Location, property.Location);
        Console.WriteLine($"Distance between {baseProperty.Address} and {property.Address}: {distance:F2} meters");
    }

    // 这里可以加保存计算结果、更新UI等后续逻辑
}

3. 调用时的正确姿势:避免死锁

在UI线程调用批量方法时,一定要用await,不要用.Wait().Result,否则会导致死锁:

// 比如按钮点击事件(这里用async void是合理的,因为是事件处理)
private async void btnCalculateDistances_Click(object sender, EventArgs e)
{
    // 替换成你的地址列表
    var addresses = new List<string> 
    { 
        "123 Main St, New York", 
        "456 Oak Ave, Brooklyn", 
        "789 Pine Rd, Queens" 
    };

    await BatchProcessAddressesAsync(addresses);
    MessageBox.Show("所有地址解析和距离计算完成!");
}
关键说明
  • 找到匹配后停止当前任务:在SearchSubjectAddressAsync中,拿到匹配结果后直接return,当前异步任务就会终止,不会执行后续冗余逻辑。
  • 所有任务完成后触发后续方法BatchProcessAddressesAsync中,等foreach里的所有await都执行完毕,才会调用距离计算方法,完全避免了“后续方法提前执行”的问题。
  • 为什么之前改Task会无限等待:因为在UI线程用.Wait()/.Result会阻塞UI线程,而异步任务需要UI线程继续执行后续逻辑,形成死锁,改用await就能让线程正确协作。

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

火山引擎 最新活动