2012-10-17 5 views
7

आप एक साथ कई HttpClient.GetAsync() अनुरोध कैसे शुरू करते हैं, और जैसे ही उनके संबंधित प्रतिक्रियाएं वापस आती हैं उन्हें संभालें? सबसे पहले मैंने कोशिश की है:कई एसिंक/प्रतीक्षा कार्यों को एक साथ शुरू करना और उन्हें अलग से संभालना

var response1 = await client.GetAsync("http://example.com/"); 
var response2 = await client.GetAsync("http://stackoverflow.com/"); 
HandleExample(response1); 
HandleStackoverflow(response2); 

लेकिन निश्चित रूप से यह अभी भी अनुक्रमिक है। तो फिर मैं उन दोनों को एक ही बार में शुरू करने की कोशिश की:

var task1 = client.GetAsync("http://example.com/"); 
var task2 = client.GetAsync("http://stackoverflow.com/"); 
HandleExample(await task1); 
HandleStackoverflow(await task2); 

अब कार्य एक ही समय है, जो अच्छा है पर शुरू कर दिया है, लेकिन निश्चित रूप से कोड को अभी भी एक के बाद एक के लिए इंतजार करना पड़ता है।

मैं क्या चाहता हूँ के रूप में यह में आता है, और "stackoverflow.com" जैसे ही यह में आता प्रतिक्रिया के रूप में जल्द ही "example.com" प्रतिक्रिया को संभालने के लिए सक्षम होने के लिए है।

मैं डाल सकता है एक सरणी में दो कार्य एक लूप में Task.WaitAny() का उपयोग करते हैं, यह जांचते हुए कि कौन सा पूरा हो गया है और उचित हैंडलर को कॉल करें, लेकिन फिर ... नियमित रूप से पुराने कॉलबैक से बेहतर कैसे है? या यह वास्तव में async/प्रतीक्षा के लिए एक इच्छित उपयोग केस नहीं है? यदि नहीं, तो मैं कॉलबैक के साथ HttpClient.GetAsync() का उपयोग कैसे करूं?

स्पष्ट करने के लिए - व्यवहार मैं के बाद कर रहा हूँ इस छद्म कोड की तरह कुछ है: WhenAll

client.GetAsyncWithCallback("http://example.com/", HandleExample); 
client.GetAsyncWithCallback("http://stackoverflow.com/", HandleStackoverflow); 

उत्तर

12

आप ContinueWith उपयोग कर सकते हैं और इंतजार करने के लिए एक नया Task, task1 और task2 समानांतर

में क्रियान्वित की जाएगी
var task1 = client.GetAsync("http://example.com/") 
        .ContinueWith(t => HandleExample(t.Result)); 

var task2 = client.GetAsync("http://stackoverflow.com/") 
        .ContinueWith(t => HandleStackoverflow(t.Result)); 

var results = await Task.WhenAll(new[] { task1, task2 }); 
+4

FWIW, कार्य के बाद से। जब सभी ओवरले लोड में पैरामीटर का उपयोग करते हैं, तो आप केवल आखिरी पंक्ति को जब सभी (task1, task2) में बदल सकते हैं और कंपाइलर को आपके लिए सरणी बना सकते हैं :) http://msdn.microsoft .com/en-us/पुस्तकालय/hh194874।एएसपीएक्स –

+1

यह उत्तर सिर्फ दोनों को पूरा करने के लिए इंतजार कर रहा है। यह "जैसे ही उनके संबंधित प्रतिक्रिया वापस आते हैं" उन्हें "प्रत्येक को संभाल नहीं लेते"। –

+2

[मुझे पता है @ स्टीफन क्लेरी की टिप्पणी वास्तव में पुरानी है, लेकिन मैं नहीं चाहता था कि कोई भी उलझन में आने के लिए एक ही चीज़ सोच रहा हो।] यह उत्तर उन्हें प्रत्येक को अपने 'जारी रखें' कॉल में संभालता है (जिसका परिणाम वास्तविक कार्यों को ' कार्य 1' और 'task2')। प्रतीक्षित 'कब सभी' बस यह सुनिश्चित करता है कि उन दोनों "हैंडलिंग" कार्यों को निष्पादित किए जाने से पहले किसी भी पंक्ति से पहले किया जाता है। – patridge

4

एक async समारोह घोषित और में अपने कॉलबैक पारित:

void async GetAndHandleAsync(string url, Action<HttpResponseMessage> callback) 
{ 
    var result = await client.GetAsync(url); 
    callback(result); 
} 

और फिर बस इसे कहते कई बार:

GetAndHandleAsync("http://example.com/", HandleExample); 
GetAndHandleAsync("http://stackoverflow.com/", HandleStackoverflow); 
5

आप एक विधि है कि फिर से आदेश होगा उन्हें वे पूरा के रूप में उपयोग कर सकते हैं। यह Jon Skeet और Stephen Toub द्वारा वर्णित एक अच्छी चाल है, और यह भी मेरे AsyncEx library द्वारा समर्थित है।

सभी तीन कार्यान्वयन बहुत समान हैं। मेरे अपने कार्यान्वयन ले रहा है:

/// <summary> 
/// Creates a new array of tasks which complete in order. 
/// </summary> 
/// <typeparam name="T">The type of the results of the tasks.</typeparam> 
/// <param name="tasks">The tasks to order by completion.</param> 
public static Task<T>[] OrderByCompletion<T>(this IEnumerable<Task<T>> tasks) 
{ 
    // This is a combination of Jon Skeet's approach and Stephen Toub's approach: 
    // http://msmvps.com/blogs/jon_skeet/archive/2012/01/16/eduasync-part-19-ordering-by-completion-ahead-of-time.aspx 
    // http://blogs.msdn.com/b/pfxteam/archive/2012/08/02/processing-tasks-as-they-complete.aspx 

    // Reify the source task sequence. 
    var taskArray = tasks.ToArray(); 

    // Allocate a TCS array and an array of the resulting tasks. 
    var numTasks = taskArray.Length; 
    var tcs = new TaskCompletionSource<T>[numTasks]; 
    var ret = new Task<T>[numTasks]; 

    // As each task completes, complete the next tcs. 
    int lastIndex = -1; 
    Action<Task<T>> continuation = task => 
    { 
    var index = Interlocked.Increment(ref lastIndex); 
    tcs[index].TryCompleteFromCompletedTask(task); 
    }; 

    // Fill out the arrays and attach the continuations. 
    for (int i = 0; i != numTasks; ++i) 
    { 
    tcs[i] = new TaskCompletionSource<T>(); 
    ret[i] = tcs[i].Task; 
    taskArray[i].ContinueWith(continuation, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); 
    } 

    return ret; 
} 

फिर आप यह इस तरह के रूप में उपयोग कर सकते हैं:

var tasks = new[] 
{ 
    client.GetAsync("http://example.com/"), 
    client.GetAsync("http://stackoverflow.com/"), 
}; 
var orderedTasks = tasks.OrderByCompletion(); 
foreach (var task in orderedTasks) 
{ 
    var response = await task; 
    HandleResponse(response); 
} 

एक और दृष्टिकोण TPL Dataflow उपयोग करने के लिए है,

var block = new ActionBlock<string>(HandleResponse); 
var tasks = new[] 
{ 
    client.GetAsync("http://example.com/"), 
    client.GetAsync("http://stackoverflow.com/"), 
}; 
foreach (var task in tasks) 
{ 
    task.ContinueWith(t => 
    { 
    if (t.IsFaulted) 
     ((IDataflowBlock)block).Fault(t.Exception.InnerException); 
    else 
     block.Post(t.Result); 
    }); 
} 

ऊपर जवाब में से किसी भी तरीके से कार्य करेंगे: के रूप में प्रत्येक कार्य को पूरा करता, इसके संचालन एक ActionBlock<T> करने के लिए, कुछ इस तरह पोस्ट। यदि आपका शेष कोड उपयोग करता है/टीपीएल डेटाफ्लो का उपयोग कर सकता है, तो आप उस समाधान को प्राथमिकता दे सकते हैं।

+0

स्टीफन, क्या आप कृपया बता सकते हैं कि एक ही एसिंक विधि के भीतर इंतजार किए जाने पर कई कार्य क्यों पूरा हो जाएंगे, जैसे ही वे पूरा हो जाएंगे? जैसे 'foreach (कार्यों में var टी) टी इंतजार;' और मान लीजिए 't1'' t0' से पहले पूरा हो जाता है; लेकिन 't1' परिणाम तब तक नहीं दिखाया जाएगा जब तक कि 't0' परिणाम पहले दिखाया न जाए। – stt106

+0

क्योंकि 'प्रतीक्षा टी 1' निष्पादित नहीं किया जाएगा जब तक कि' प्रतीक्षा टी 0 'पूरा नहीं हो जाता है। –

+0

आह मुझे लगता है कि मुझे पता है कि मैं उलझन में क्यों था लेकिन बस स्पष्ट होने के लिए, इन सभी कार्यों को अभी भी असीमित रूप से निष्पादित किया गया है लेकिन उनके परिणाम वास्तव में क्रमशः संसाधित होते हैं क्योंकि प्रत्येक कार्य परिणाम पिछले कार्य की निरंतरता बन जाता है? अर्थात् इन सभी कार्यों को चलाने के लिए जितना समय लगता है, वह एक ही कार्य के समान होता है जो अधिकतर समय लगता है, प्रत्येक कार्य चलने वाले समय की राशि नहीं। – stt106

संबंधित मुद्दे