在执行get方法的异步调用时在主代码中等待

本文关键字:代码 等待 异步 执行 get 方法 调用 | 更新日期: 2023-09-27 17:50:31

void main() {
    OpenWeatherApiClient.getCurrentLocationWeatherAsync(55.513434, -37.53434, (x) => {
        Console.WriteLine(x.coord.lat);
        Console.ReadLine();
    });
}
Console.WriteLine("this should happen before");
/// I want to hold the the function here till the call back returns and executes the above logic
/// the asynchronous function
public static void getCurrentLocationWeatherAsync(double latitude, double longitude, Action<WeatherData> callback) {
    //weather from one station
    string weatherSearch = "weather?lat={0}&lon={1}";
    var url = string.Concat(baseUrl, weatherSearch);
    //Customize the URL according to the geo location
    url = string.Format(url, latitude, longitude);
    //Syncronous consumption
    var asynClient = new WebClient();
    //add Appid for verification
    asynClient.Headers.Add(APPIDName, APPID);
    asynClient.OpenReadCompleted += (o, a) => {
        if (callback != null) {
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeatherData));
            callback(ser.ReadObject(a.Result) as WeatherData);
        }
    };
    asynClient.OpenReadAsync(new Uri(url));
}

我有女仆这个异步get方法,解析成数据合同后的请求。虽然我想保持主方法,直到getcall和关联代码在委托完全执行。在未来,我想要的是并发调用多于get方法,并保持它们,直到它们全部执行。之后,我将在主程序中继续,因为每个get调用的数据都是依赖的

在执行get方法的异步调用时在主代码中等待

如果我没理解错的话:

private static readonly List<Task> weatherTasks = new List<Task>(); 
public static void GetCurrentLocationWeatherAsync(double latitude, double longitude, Action<WeatherData> callback)
{
    // ...
    weatherTasks.Add(asynClient.OpenReadTaskAsync(new Uri(url)));
}
public static void WaitForAllWeatherCalls()
{
    Task.WaitAll(weatherTasks.ToArray());
    weatherTasks.Clear();
}

创建任务列表,然后将OpenReadAsync更改为OpenReadTaskAsync,并将任务放入列表中。方法WaitForAllWeatherCalls然后只是等待所有当前运行的任务,然后清除任务(请注意,代码不是线程安全的)。

为什么这个代码不是线程安全的:假设我们有一个线程a和一个线程b。a多次调用GetCurrentLocationWeatherAsync,然后想要等待这些调用。所以线程A调用WaitForAllWeatherCalls,现在正在等待。当线程A在等待时,线程B也想获得一些天气数据。线程B多次调用GetCurrentLocationWeatherAsync。任务列表中现在包含了一些线程A的任务和一些线程b的任务。当线程A的等待结束后,问题出现了,因为任务列表将被清除。当线程B想要等待他所有的数据时,列表中不会有任何任务,线程B甚至不会暂停一次。

更好的版本:

class WeatherGatherer
{
    private readonly List<Task> weatherTasks = new List<Task>();
    public void GetCurrentLocationWeatherAsync(double latitude, double longitude, Action<WeatherData> callback)
    {
        // ...
        weatherTasks.Add(asynClient.OpenReadTaskAsync(new Uri(url)));
    }
    public void WaitForAllWeatherCalls()
    {
        Task.WaitAll(weatherTasks.ToArray());
        weatherTasks.Clear();
    }
}

洞的东西现在是一个类,而不是静态的。类本身仍然不是线程安全的,但如果你总是为一组'weather-data gathering'创建一个实例,你就不会有问题。

请记住,只有当你有多个线程时,线程安全才是一个问题。

任何替代上述逻辑的方法也会非常好

我之前是这样做的,但不能继续,因为事件不会返回反序列化的对象

//public static async Task<WeatherData> getCurrentLocationWeatherAsync(double latitude, double longitude)
    //{
    //    //weather from one station
    //    string weatherSearch = "weather?lat={0}&lon={1}";
    //    var url = string.Concat(baseUrl, weatherSearch);
    //    //Customize the URL according to the geo location
    //    url = string.Format(url,latitude, longitude);
    //    //Syncronous consumption
    //     var asynClient = new WebClient();
    //    //add Appid for verification
    //    asynClient.Headers.Add(APPIDName,APPID);
    //    asynClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(asyncClient_DownloadStringCompleted);
    //    // API call
    //     var response = await asynClient.DownloadStringTaskAsync(url);
    //    //content=content.Replace("3h", "precipitation__3h");
    //    //create Json Serializer and parse the response

    //}
    //static void asyncClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    //{
    //    // Create the Json serializer and parse the response
    //    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(WeatherData));
    //    using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(e.Result)))
    //    {
    //        // deserialize the JSON object using the WeatherData type.
    //        var weatherData = (WeatherData)serializer.ReadObject(ms);
    //        // return weatherData;
    //    }
//}