2017-10-13 40 views
0

我是c#线程世界的新手。我读过有不同的方式来执行线程,如顺序。在C#中进行线程调用

我的场景如下。哪一个更适合以下。

我有复杂的对象列表。我将分别为每个对象[put的主体]调用PUT端点。列表中可能有1000个或更多的对象。我无法将所有对象都传递给一个对象,因此我必须在每次调用put对象时传递每个对象。这样,如果有1000个对象,我必须分别拨打1000个电话。

每次拨打电话都是相互独立的,而我必须存储每次通话回复的属性。

我正在考虑将线程概念应用到上面,但不确定哪一个以及如何去做。

任何建议将不胜感激。

预先感谢。

根据以下评论, 将方法签名放在此处并添加更多详细信息。我有IEnumerable<CamelList>。对于每个骆驼来说,我必须做出一个put请求调用,并从每个调用的响应中更新表格。我会写一个新的方法来接受这个列表,并使用下面的2个方法来调用和更新表。我必须确保,我同时进行的呼叫次数不超过100次,并且我打电话的API可以每分钟100次由同一用户呼叫。

我们有一个方法 public Camel SendRequest(handler, uri, route, Camel); //basically takes all the parameters and provide you the Camel.

我们有一个方法public void updateInTable(Entity Camel); //updates the table.

+0

首先,最早支持的.NET版本是4.5.2,其中使用'HttpClient'进行HTTP调用。其次,HttpClient的方法是* all *异步,例如'HttpClient.PutAsync()'将在后台运行。你不需要使用线程来让它们在后台运行。你甚至可以写'myUrlObjectPairss.Select(data => client.PutAsync(data.url,data.Content))。ToArray()'并行启动所有调用 –

+0

我的不好。我们正在使用c#6。我错误地添加了其他C#4之一。 – Vicky

+0

@Vicky - 您添加的签名中的类型不一致。没有办法,这是你的真实代码。如果您需要帮助,请尽量让我们尽可能轻松地回答。我们需要一个[mcve]。 – Enigmativity

回答

1

HTTP调用使用HttpClient class,其HTTP方法已经异步通常由。您不需要创建自己的线程或任务。

所有异步方法返回一个TaskTask<T> value. You need to use the等待keyword to await for the operation to complete asynchronously - that means the thread is released until the operation completes. When that happens, execution resumes after the等待。

您可以看到如何编写PUT请求here。该示例使用PutAsJsonAsync方法来减少序列化Product类转换成字符串所需的样板代码,并创建一个StringContent类与正确的内容类型,例如:

var response = await client.PutAsJsonAsync($"api/products/{product.Id}", product); 
response.EnsureSuccessStatusCode(); 

如果你想要把1000级的产品,你需要的是一个数组或列表中的产品。您可以使用LINQ进行多个呼叫,等待他们的任务返回结尾:

var callTasks = myProducts.Select(product=>client.PutAsJsonAsync($"api/products/{product.Id}", product); 
var responses = await Task.WhenAll(callTasks); 

这意味着你必须等待所有请求完成之前,你可以检查有没有人成功。你可以改变的Select身体等待响应本身:

var callTasks = myProducts.Select(async product=>{ 
     var response=await client.PutAsJsonAsync($"api/products/{product.Id}", product); 
     if (!response.IsSuccessStatusCode) 
     { 
      //Log the error     
     } 
     return response.StatusCode; 
}); 
var responses=await Task.WhenAll(callTasks); 

这是更好地虽然CONVER拉姆达到一个单独的方法,如PutProductAsync:

async Task<HttpStatusCode> PutProduct(Product product,HttpClient client) 
{ 
      var response=await client.PutAsJsonAsync($"api/products/{product.Id}", product); 
      if (!response.IsSuccessStatusCode) 
      { 
       //Log the error     
      } 
      return response.StatusCode; 
}; 

var callTasks = myProducts.Select(product=>PutProductAsync(product)); 
var responses=await Task.WhenAll(callTasks); 
+0

非常感谢@Panagiotis Kanavos。唯一的问题是,我打电话或消费的API只允许来自特定ID的100个电话一分钟左右。在这种情况下,将等待时间或thread.sleep设置为例如两倍的时间会好吗?在接下来的100个电话之前2分钟。我只是考虑一些缓冲时间而说双倍。或者还有其他更好的方法来做到这一点。 – Vicky

0

我会用暗示微软对此的反应框架。你需要NuGet“System.Reactive”来获取这些位。

然后就可以做到这一点:

var urls = new string[1000]; //somehow populated; 

Func<string, HttpContent, IObservable<string>> putCall = (u, c) => 
    Observable 
     .Using(
      () => new HttpClient(), 
      hc => 
       from resp in Observable.FromAsync(() => hc.PutAsync(u, c)) 
       from body in Observable.FromAsync(() => resp.Content.ReadAsStringAsync()) 
       select body); 

var callsPerTimeSpanAllowed = 100; 
var timeSpanAllowed = TimeSpan.FromMinutes(1.0); 

IObservable<IList<string>> bufferedIntervaledUrls = 
    Observable.Zip(
     Observable.Interval(timeSpanAllowed), 
     urls.ToObservable().Buffer(callsPerTimeSpanAllowed), 
     (_, buffered_urls) => buffered_urls); 

var query = 
    from bufferedUrls in bufferedIntervaledUrls 
    from url in bufferedUrls 
    from result in putCall(url, new StringContent("YOURCONTENTHERE")) 
    select new { url, result }; 

IDisposable subscription = 
    query 
     .Subscribe(
      x => { /* do something with each `x.url` & `x.result` */ }, 
      () => { /* do something when it is all finished */ }); 

该代码被打破网址成块(或缓冲器)100,并把它们放在开1分钟的时间线(或间隔)。然后它会为每个URL调用putCall并返回结果。

现在它可能有点先进,但我认为这个答案可能是有用的,只是看看这可能是多么的干净。

+0

谢谢@Enigmativity。我不熟悉system.reactive扩展,因此我正在阅读它。看起来像一个好方法。但是,我的团队建议我使用Parallel.Foreach进行线程处理,这对我来说又是新的。我试图看看如何实现这一点,以及如果不是一个好方法来找出原因的优点和缺点。以前从未应用过他们。让我知道如果你知道如何使用parallel.foreach的方式来实现它。如果除401,403或某些真实状态代码之外,响应中有任何问题,我可能需要添加重试功能。 – Vicky

+0

@Vicky - 你不能通过'Parallel.For'缓冲时间。你需要将你原来的'urls'列表分成100个块,并在它们周围放置一个计时器 - 然后你可以使用'Parallel.For'。你仍然需要管理定时器和分组 - 我的代码很容易实现。 – Enigmativity

+0

噢,好吧。说得通。我们已经有了一个post方法,在这个方法中,我只需要传递一个对象类型,我期望在一个响应中,url,httpClientHandler返回反序列化的对象。另外,一旦收到响应,我需要在Azure表存储中存储一些属性。我如何更新上面的代码来做到这一点?因为,我在ToObservable.Using(......)中看到;作为IObservable接口的一些内置方法。 – Vicky