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

waiting in the main code while asynchronous call for get method executes

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));
}

在将请求解析为数据契约后,我已经处理了这个异步获取方法。虽然我想保留主要方法,直到完全执行委托中的 getcall 和关联代码。 将来我想要的是同时调用不仅仅是 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。 Task列表现在包含线程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();
    }
}

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

请记住,线程安全只有在您拥有多个线程时才会成为问题。

执行相同上述逻辑的任何替代方案也非常好

我以前是这样做的,但无法继续,因为事件不会return反序列化对象

//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;
    //    }
//}