2017-10-13 48 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