简体   繁体   English

在Async-await C#中使用多线程

[英]Using Multithreading with Async-await C#

I wrote an async function for calling data from Facebook, it works, but the problem is I dun suppose it works. 我写了一个异步函数来从Facebook调用数据,它可以工作,但是问题是我认为它可以工作。 Can someone explain to me? 有人可以向我解释吗?

public class FacebookData
    {
        static string fb_api_version = ConfigurationManager.AppSettings["fb_ver"];
        static string accessToken = ConfigurationManager.AppSettings["accessToken"];
        static string fb_id = "";
        private HttpClient _httpClient;
        public FacebookData(string input_id)
        {
            fb_id = input_id;
            _httpClient = new HttpClient
            {
                BaseAddress = new Uri("https://graph.facebook.com/" + fb_api_version + "/"),
                Timeout = TimeSpan.FromSeconds(15)
            };
            _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }

        public async Task<T> getData<T>()
        {
            var response = await _httpClient.GetAsync($"{fb_id}?access_token={accessToken}");
            if (!response.IsSuccessStatusCode)
                return default(T);

            var result = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<T>(result);
        }
    }

The calling class is typical, I make it await for the response. 调用类是典型的,我让它等待响应。

But the problem is where I call it. 但是问题出在哪里。

In main 在主要

static void Main(string[] args)
{
   string[] data_Set = [//ids_group]

   for (int i = 0; i < data_Set.length; ++i){
       Console.WriteLine("Running Thread " + (i+1).ToString());
       var dataSet = facebookRequestCmd(data_Set[i]);
       writeToTXT(dataSet);
       Console.WriteLine("Finished Thread " + (i + 1).ToString());
       //do sth
   }
}

In facebookRequestCmd 在facebookRequestCmd中

static Dictionary<string, string[]> facebookRequestCmd(string ids){
   Dictionary<string, string[]> allData = new Dictionary<string, string[]>();
   string[] ids_arr = ids.split(",")
   for (var i = 0; i < ids.length; i++){
      var facebook_client = new FacebookData(sqlData);
      var response = facebook_client.getData<dynamic>();
      Task.WaitAll(response);

      //then use the result to do sth
   }
}

In my understanding, each time I call getData, it already come back to main thread, as it is awaiting the data. 据我了解,每次调用getData时,它都在等待数据时已经返回主线程。 So the Task doesn't really start a new thread. 因此,该Task并未真正启动新线程。

Thus, async await works for waiting the http request, but the Threading should not work. 因此,async await用于等待http请求,但是Threading不起作用。

However, 然而,

Console.WriteLine("Running Thread " + (i+1).ToString());

jumps out simultaneously like I really make the Thread in the for loop in main function. 就像我真的在main函数的for循环中创建Thread一样同时跳出。

Why? 为什么? And is that the way to use Multithreading with Async-await. 这就是在异步等待中使用多线程的方式。 As I want to make multiple calls at the same time. 由于我想同时拨打多个电话。

Originally I use Parallel.ForEach to kick starting the calling, however, thats not asynchronous and will block the thread. 最初,我使用Parallel.ForEach来启动调用,但是那不是异步的,并且会阻塞线程。

Ok, feel free to ignore all the changes I've made but I couldn't help but modify the way some of the variables read and the code looked. 好的,可以随意忽略我所做的所有更改,但是我不得不修改某些变量的读取方式和代码的外​​观。 This is not a working application and I have obviously not tested it. 这不是一个有效的应用程序,我显然没有对其进行测试。 This is just a cleaned up version of what you have with a suggested way of using Task . 这只是使用Task的建议方式清除的内容。 It's also mocked using just the code you've provided so it is what it is. 它也仅使用您提供的代码进行了模拟,因此就是它的样子。 #2 is, what I believe, the answer you needed. #2是我所相信的,您需要的答案。

  1. In Main I removed the words 'thread' since that's not actually what's happening. Main我删除了“线程”一词,因为这实际上并没有发生。 It may be, but we don't know if the HttpClient is indeed starting a new thread or just holding / returning from the rest call. 可能是,但是我们不知道HttpClient是否确实在启动一个新线程,或者只是保持/从rest调用返回。 Using async / await does not always mean a Thread was started (although it's common to think of it that way). 使用async / await并不总是意味着启动了一个Thread (尽管通常以这种方式来考虑)。
  2. I used .Result (not Wait() as I suggested in comments) to get the result of the task. 我使用.Result (不是我在注释中建议的Wait() )来获取任务的结果。 This is ok since it's a console app but not ideal for a real world app that needs to operate without blocking. 可以,因为它是一个控制台应用程序,但对于需要无障碍运行的现实世界应用程序来说并不理想。 I also removed Task.WaitAll with this change. 我还通过此更改删除了Task.WaitAll
  3. I renamed functions to have verbage because, IMO, functions should be doing work and the naming should describe the work being done. 我将函数重命名为具有惯用语,因为在IMO中,函数应该正在工作,而命名应描述正在完成的工作。
  4. I renamed some variables because, IMO, variables should be PascalCase when their scope isn't in a method or private and camelCase when they are. 我重命名了一些变量,因为在IMO中,当变量的作用域不在方法中时,变量应为PascalCase;在变量的作用域内时,变量应为private;而camelCase则应为骆驼式。 The names should also, IMO, be what it is followed by the Type that makes sense. 国际海事组织的名称也应该是紧随其后的有意义的Type
  5. I appended 'Async' to function names that return a running Task . 我在返回运行中Task函数名称后附加了“异步”。
  6. Changed FacebookClient to be singleton and allowing only one HttpClient to be used instead of many and allowing it to be disposed; FacebookClient更改为单例,只允许使用一个HttpClient而不是多个HttpClient并允许将其处置; plus more. 还有更多。
  7. Added alternate version of the GetFacebookData function that calls the tasks and awaits them all simultaneously. 添加了GetFacebookData函数的替代版本,该函数调用任务并同时等待所有任务。

static void Main(string[] args)
{
    string[] dataSet = new string[] { /* mocked */ };  // [ids_group]; <- no idea what this is so I mocked it.

    for (int i = 0; i < dataSet.Length; i++)
    {
        Console.WriteLine("Main... " + (i + 1).ToString());
        var result = GetFacebookData(dataSet[i]);
        WriteToTxt(result);
        Console.WriteLine("Complete... " + (i + 1).ToString());
        //do sth
    }

    Console.Read();
}

private static Dictionary<string, string[]> GetFacebookData(string idsString)
{
    var allDataDictionary = new Dictionary<string, string[]>();
    var idsArray = idsString.Split(',');

    foreach (var id in idsArray)
    {
        var response = FacebookClient.Instance.GetDataAsync<string[]>(id).Result;
        allDataDictionary.Add(id, response);
    }

    return allDataDictionary;
}

public class FacebookClient
{
    private readonly HttpClient httpClient;
    private readonly string facebookApiVersion;
    private readonly string accessToken;

    public static FacebookClient Instance { get; } = new FacebookClient();

    FacebookClient()
    {
        facebookApiVersion = ConfigurationManager.AppSettings["fb_ver"];
        accessToken = ConfigurationManager.AppSettings["accessToken"];

        httpClient = new HttpClient
        {
            BaseAddress = new Uri("https://graph.facebook.com/" + facebookApiVersion + "/"),
            Timeout = TimeSpan.FromSeconds(15)
        };

        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    }

    public async Task<T> GetDataAsync<T>(string facebookId)
    {
        var response = await httpClient.GetAsync($"{facebookId}?access_token={accessToken}");
        if (!response.IsSuccessStatusCode) return default;
        var result = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(result);
    }

    ~FacebookClient() => httpClient.Dispose();
}

Here's a version that's starting all the tasks and then awaiting them all at the same time. 这是一个开始所有任务然后同时等待所有任务的版本。 I believe this might give you some issues on the HttpClient but we'll see. 我相信这可能会给您HttpClient带来一些问题,但是我们会看到的。


private static Dictionary<string, string[]> GetFacebookData(string idsString)
{
    var allDataDictionary = new Dictionary<string, string[]>();
    var idsArray = idsString.Split(',');
    var getDataTasks = new List<Task<string[]>>();

    foreach (var id in idsArray)
    {
        getDataTasks.Add(FacebookClient.Instance.GetDataAsync<string[]>(id));         
    }

    var tasksArray = getDataTasks.ToArray();
    Task.WaitAll(tasksArray);
    var resultsArray = tasksArray.Select(task => task.Result).ToArray();

    for (var i = 0; i < idsArray.Length; i++)
    {
        allDataDictionary.Add(idsArray[i], resultsArray[i]);
    }

    return allDataDictionary;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM