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

waiting in the main code while asynchronous call for get method executes
2020-10-25
  •  译文(汉语)
  •  原文(英语)
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方法提供了服务.虽然我想保留main方法,直到委托中的getcall和关联代码完全执行.将来,我想要的是并发调用而不是获取方法,并保持它们直到它们全部执行.之后,我将继续进行主要操作,因为每个get调用的数据都取决于

速聊1:
当您实际上要保留执行线程时,为什么要运行异步代码?
速聊2:
对不起,我没有明白这一点.但是我正在使用异步服务调用,因为我必须调用多个服务方法,并且不想等待第一个调用的响应,然后再继续第二个调用
速聊3:
您想getCurrentLocationWeatherAsync多次调用该方法,然后等到所有方法都完成后,还是我理解您的问题错了?
速聊4:
是的,想法是相同的,但是我想调用另外两个方法getCityWeatherDatAsync和getFivehourlyDataAsync.在所有这些方法返回时,请等待主代码.
解决过程1

如果我正确了解您:

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

现在,漏洞是在类中,而不是静态的.该类本身仍然不是线程安全的,但是如果您始终为一组"天气数据收集"创建一个实例,则不会有问题.

请记住,只有多个线程时,线程安全才是问题.

速聊1:
谢谢,这已经开始起作用.我对此仍然很了解,所以您能解释一下代码不是线程安全的意思是什么,我如何使其更安全
速聊2:
我添加了一个解释,但请记住,如果您使用的线程不止一个,则可以简单地使用较高版本.
速聊3:
谢谢你,这对我有很大帮助
解决过程2

任何与上述逻辑相同的替代方法也非常好

我以前是这样做的,但是无法继续,因为该事件不会返回反序列化的objec

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

I have maid this asynchronous get method, after parsing the request into data contract. While i want to hold the main method till the getcall and associate code in the delegate are completely executed. In future what i want is to concurrently call more than get methods and hold them till all of them execute. After which i would proceed in the main since the data from each get call is dependent

Talk1:
Why do you run asynchronous code when you actually want to hold the executing thread?
Talk2:
Sorry i did not get the point. But I am using the async service calls because i have to call multiple service methods and do not want to wait for response of the first call and then move on two the second
Talk3:
You want to call the getCurrentLocationWeatherAsync method multiple times and then wait until all of them finished or am I understanding your question wrong?
Talk4:
Yes, the idea is same but I want to call two other methods getCityWeatherDatAsync and getFivehourlyDataAsync. And wait in the main code while all of these methods return.
Solutions1

If I am understanding you correctly:

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

Create a list of tasks then change the OpenReadAsync to OpenReadTaskAsync and put the Task into the list. The method WaitForAllWeatherCalls is then just waiting for all currently running tasks and is then clearing the tasks (be aware that the code is not thread safe).

Why this code is not thread-safe: Lets say we have a Thread A and a Thread B. A calls the GetCurrentLocationWeatherAsync multiple times then wants to wait for these calls. So thread A calls WaitForAllWeatherCalls and is now waiting. While thread A is waiting thread B also wants to get some weather data. Thread B calls GetCurrentLocationWeatherAsync multiple times. The Task list now contains some tasks of thread A and some of thread B. The problem comes up when the waiting of thread a is over, because the task list will be cleared. When thread B wants to wait on all his data there will not be any tasks in the list and thread B won't even pause once.

A better version:

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

The hole thing is now in a class instead of being static. The class itself is still not thread-safe but if you always creating one instance for one group of 'weather-data gathering' you won't have a problem.

Remember that thread-safety is only a problem when you have more than one thread.

Talk1:
Thank you this has started to work. I am still knew to this so can you explain what do you mean by code is not thread safe and how can i make it more safer
Talk2:
I added an explanation but remember that if you are not using more than one thread you can simply use the upper version.
Talk3:
Thank you this helped me a lot
Solutions2

Any alternatives to do the same above logic would also be really great

I was previously doing it this way but could not continue because the event would not return deserialized objec

//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;
    //    }
//}
转载于:https://stackoverflow.com/questions/30424634/waiting-in-the-main-code-while-asynchronous-call-for-get-method-executes

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入