2015-06-01 226 views
1

我有一个Web服务,加载一些插件(DLL),并调用他们的过程方法。其中一个插件获取成员列表,并确保它们都包含在MailChimp列表中。与HTTPClient foreach异步/等待

这里是,增加了用户的MailChimp组的代码。

private async Task AddMCUsers(List<Member> _memberList) 
    { 
     using (var http = new HttpClient()) 
     { 
      var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user:password"); 
      http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", creds); 
      string memberURI = string.Format(@"{0}lists/{1}/members", _baseURI, _memberGroupId); 
      var jss = new JavaScriptSerializer(); 

      foreach (var user in _memberlist) 
      { 
       var _addStatus = ""; 

       try 
       { 
        var content = jss.Serialize(new MCPost() 
        { 
         email_address = user.Email, 
         status = "subscribed", 
         merge_fields = new MCMergeFields() 
         { 
          FNAME = user.Firstname, 
          LNAME = user.Lastname 
         } 
        }); 

        using(var result = await http.PostAsync(memberURI, new StringContent(content,Encoding.UTF8, "application/json"))) 
        { 
         var resultText = await result.Content.ReadAsStringAsync(); 

         if(result.IsSuccessStatusCode) 
         { 
          _addStatus = "Success"; 
          var _returnedUser = jss.Deserialize<MCMember>(resultText); 
          //Store new user's id 
          user.ServiceId = _returnedUser.id; 
         } 
         else 
         { 
          _addStatus = "Fail"; 
         } 
        } 
       } 
       catch { 
        _addStatus = "Error"; 
       } 

       LogEvent("Add User - " + _addStatus, string.Format("Id: {0} - {1} {2} (Account: {3}) : {4}", user.Id, user.Firstname, user.Lastname, user.AccountId, user.Email)); 

      } 
     } 
    } 

在正常的程序代码中,这不成问题。但是,httpClient上唯一可用的Post方法是PostAsync。对于异步/等待的东西相当陌生,我不确定其余代码的分歧......特别是因为它涉及到我重复使用httpClient而不是为每个http调用实例化一个新代码。

我不知道用的await会发生什么时,其在foreach裹得像我有。在异步运行时,我会遇到重复使用httpClient重复调用的问题吗?

我的另一个问题是,什么是真正要被退回。 IOW,我的理解是等待回报一个任务。但是,在这里,我正在循环访问列表并进行多个调用以等待PostAsync。我的方法返回一个任务。但是,返回哪个任务?如果我的调用方法在继续之前需要等待完成,它的调用是什么样子的?

private void Process() 
{ 
    //Get List 

    var task = AddMCUsers(list); 
    task.Wait(); 

    //Subsequent processing 
} 

我读过你应该一直使用异步。这是否意味着我的调用方法应该看起来更像这样?

public async Task Process() 
{ 
    //Get list 
    ... 
    await AddMCUsers(list); 

    //Other processing 
    } 

感谢您提供的任何帮助。

+1

只是一个快速建议 - 请不要做'catch {...}'或'catch(Exception ex){...}'。这只是一个非常不好的习惯,它会吞噬错误,使得代码难以调试。 – Enigmativity

回答

4

在正常程序代码,这将不会是一个问题。

async/await整点是在看起来几乎相同的“正常”同步代码的方式来写异步代码。

是相当新的异步/等待的东西,我不知道在我的代码的其余部分的后果......尤其是当它涉及到我试图重用HttpClient的,而不是用于实例化一个新的每个http调用。

HttpClient被打算重新使用;实际上,它可以同时用于任何数量的呼叫。

我不知道用的await会发生什么时,其在foreach裹得像我有。

想到它的一种方法是await“暂停”该方法,直到其操作完成。当操作完成时,该方法继续执行。我有一个async intro进入更多的细节。

当我在异步运行时重复使用httpClient进行重复调用时会遇到问题吗?

不,这很好。

IOW,我的理解是等待返回一个任务。

await需要一个Task。它“解开”该任务并返回任务的结果(如果有的话)。如果任务完成,例外,则await引发该异常。

我的方法返回一个任务。但是,返回哪个任务?

Task从由async状态机创建的async方法返回。你不必担心它。请参阅我的介绍了解更多详情。

如果我的调用方法在继续之前需要等待完成,它的调用是什么样的? ...我读过你应该一直使用Async。这是否意味着我的调用方法应该看起来更像这样?

是的,它应该看起来像你的第二个片段:

public async Task ProcessAsync() 
{ 
    //Get list 
    ... 
    await AddMCUsers(list); 

    //Other processing 
} 

我唯一改变的是Async后缀,这是由Task-based Asynchronous Pattern建议。

+0

感谢您的回答!还有一个问题,就像我编码的方式一样,HTTPClient调用都在等待彼此,而不是同时运行。 IOW,我的代码 - 等待http.PostAsync(...)在继续执行foreach之前等待通话...正确?所以在第一次完成之前它不能进行更多的HTTP调用?有没有更好的方法来处理这个问题,以使它们能够同时运行......或者并行运行 - 仍然难以理解差异 - 同时运行正是我所追求的目标。 – RHarris

+0

@RHarris:当然!首先,将你的'foreach'循环的内部因素分解为一个单独的方法,添加一个用户('AddMCUserAsync'),然后你可以用'await Task.WhenAll(_memberlist.Select(x => AddMCUserAsync(x,...)));' –

0
在你的代码

你应该罚款与重用HttpClient的。 async/await允许代码释放执行线程,以防止在等待Web响应时锁定cpu线程。它还释放控制权给调用者。当释放代码返回给调用者就意味着,如果你的Process功能不会等待你的AddMCUsersProcess可能之前AddMCUsers(有用的火灾,忘记的情况下不等待的方法)完成。

async/await不做什么是影响个人方法的逻辑流程。当您等待异步Web呼叫时,执行会暂停,然后在网络呼叫返回时在同一点继续执行。还有线程上下文跟踪,并且代码在默认情况下在相同的上下文中恢复(即,UI线程或后台线程,具体取决于父级),但是如果需要可以更改。

在代码中的某个时候你可能想有块,直到您的异步代码竞争的方法,那就是在那里你会希望你的Task.Wait()调用块的执行。如果你使用的所有东西都在等待,那么你的程序就有可能在你的任务竞争之前结束。请参阅下面的代码示例。

class Program 
{ 
    static void Main(string[] args) 
    { 
     Task waitForMe = Task.Run(() => waitAsync()); 
    } 

    static async Task waitAsync() 
    { 
     await Task.Delay(5000); 
    } 
} 

同出一Task.Wait呼叫阻止的主要方法之前5秒的等待完成该程序将结束于样品中。具有以下的主要方法将导致程序退出之前等待5秒钟:

static void Main(string[] args) 
{ 
    Task waitForMe = Task.Run(() => waitAsync()); 

    waitForMe.Wait(); 
}