2012-11-15 24 views
5

यह सवाल पिछले एक सवाल का एक अनुवर्ती है कि मैं कहा था है के रूप में ठीक काम करता है:टास्क लाइब्रेरी कोड फ़्रीज़ समानांतर एक Windows Forms में आवेदन - एक Windows कंसोल आवेदन

How to Perform Multiple "Pings" in Parallel using C#

मैं करने में सक्षम था काम करने के लिए स्वीकृत उत्तर (एक विंडोज कंसोल एप्लिकेशन) प्राप्त करें, लेकिन जब मैंने विंडोज फॉर्म एप्लिकेशन में कोड चलाने की कोशिश की, तो निम्न कोड Task.WaitAll(pingTasks.ToArray()) युक्त लाइन पर जमा हो जाएगा। यहाँ कोड है कि मैं चलाने के लिए कोशिश कर रहा हूँ है:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
using System.Net.NetworkInformation; 

namespace WindowsFormsApplication1 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 

      List<String> addresses = new List<string>(); 

      for (Int32 i = 0; i < 10; ++i) addresses.Add("microsoft.com"); 

      List<Task<PingReply>> pingTasks = new List<Task<PingReply>>(); 
      foreach (var address in addresses) 
      { 
       pingTasks.Add(PingAsync(address)); 
      } 

      //Wait for all the tasks to complete 
      Task.WaitAll(pingTasks.ToArray()); 

      //Now you can iterate over your list of pingTasks 
      foreach (var pingTask in pingTasks) 
      { 
       //pingTask.Result is whatever type T was declared in PingAsync 
       textBox1.Text += Convert.ToString(pingTask.Result.RoundtripTime) + Environment.NewLine; 

      } 

     } 

     private Task<PingReply> PingAsync(string address) 
     { 
      var tcs = new TaskCompletionSource<PingReply>(); 
      Ping ping = new Ping(); 
      ping.PingCompleted += (obj, sender) => 
      { 
       tcs.SetResult(sender.Reply); 
      }; 
      ping.SendAsync(address, new object()); 
      return tcs.Task; 
     } 

    } 

} 

किसी को भी कारण है कि यह ठंड है के रूप में किसी भी विचार है?

उत्तर

16

यह ठंडा है क्योंकि WaitAll सभी कार्यों पर इंतजार कर रहा है, और आप UI थ्रेड में हैं, इसलिए यह यूआई थ्रेड को अवरुद्ध कर रहा है। यूआई थ्रेड को अवरुद्ध करना आपके आवेदन को फ्रीज करता है।

आप क्या करना चाहते हैं, क्योंकि आप सी # 5.0 में हैं, await Task.WhenAll(...) इसके बजाय है। (आपको उस ईवेंट हैंडलर को इसकी परिभाषा में async के रूप में चिह्नित करने की भी आवश्यकता होगी।) आपको कोड के किसी अन्य पहलू को बदलने की आवश्यकता नहीं होगी। यह ठीक काम करेगा।

await वास्तव में कार्यों में "प्रतीक्षा" नहीं करेगा। यह क्या करेगा, जब यह इंतजार कर रहा है, तो यह उस कार्य को जारी रखेगा जो आप await आईएनजी (इस मामले में, जब सभी) पर करते हैं और उस निरंतरता में यह शेष विधि को चलाएगा। फिर, उस निरंतरता को तारों के बाद, यह विधि समाप्त कर देगा और कॉलर पर वापस आ जाएगा। इसका मतलब है कि UI थ्रेड अवरुद्ध नहीं है, क्योंकि यह क्लिक ईवेंट तुरंत खत्म हो जाएगा।

(अनुरोध पर) यदि आप इसे सी # 4.0 का उपयोग करके हल करना चाहते हैं तो हमें स्क्रैच से WhenAll लिखकर शुरू करना होगा, क्योंकि इसे 5.0 में जोड़ा गया था। यह वही है जो मैंने अभी उठाया है। यह लाइब्रेरी कार्यान्वयन के रूप में शायद उतना ही कुशल नहीं है, लेकिन इसे काम करना चाहिए।

public static Task WhenAll(IEnumerable<Task> tasks) 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    List<Task> taskList = tasks.ToList(); 

    int remainingTasks = taskList.Count; 

    foreach (Task t in taskList) 
    { 
     t.ContinueWith(_ => 
     { 
      if (t.IsCanceled) 
      { 
       tcs.TrySetCanceled(); 
      } 
      else if (t.IsFaulted) 
      { 
       tcs.TrySetException(t.Exception); 
      } 
      else //competed successfully 
      { 
       if (Interlocked.Decrement(ref remainingTasks) == 0) 
        tcs.TrySetResult(null); 
      } 
     }); 
    } 

    return tcs.Task; 
} 

यहाँ svick द्वारा टिप्पणी में this suggestion के आधार पर एक और विकल्प है।

public static Task WhenAll(IEnumerable<Task> tasks) 
{ 
    return Task.Factory.ContinueWhenAll(tasks.ToArray(), _ => { }); 
} 

अब जब हम WhenAll है हम सिर्फ इतना है कि, साथ ही निरंतरता उपयोग करने के लिए, await के बजाय जरूरत है। WaitAll के बजाय आप का उपयोग करेंगे:

MyClass.WhenAll(pingTasks) 
    .ContinueWith(t => 
    { 
     foreach (var pingTask in pingTasks) 
     { 
      //pingTask.Result is whatever type T was declared in PingAsync 
      textBox1.Text += Convert.ToString(pingTask.Result.RoundtripTime) + Environment.NewLine; 
     } 
    }, CancellationToken.None, 
    TaskContinuationOptions.None, 
    //this is so that it runs in the UI thread, which we need 
    TaskScheduler.FromCurrentSynchronizationContext()); 

अब आप देख क्यों 5.0 विकल्प खूबसूरत है, और यह एक यथोचित सरल उपयोग के मामले भी है।

+1

हाँ !!! मुझे 'टास्क। WaitAll() 'के बजाय' प्रतीक्षा टास्क। जब सभी()' का उपयोग करना पड़ा था ... मुझे बटन_क्लिक ईवेंट में 'async' भी जोड़ना पड़ा। मैं आपको इस प्रश्न का उत्तर देने के लिए क्रेडिट दूंगा। धन्यवाद! – HydroPowerDeveloper

+0

पूर्णता के लिए, क्या हम 5.0 से पहले सी # का उपयोग करते समय वैकल्पिक समाधान प्राप्त कर सकते हैं? – Pete

+0

मैं दूसरा पीट का अनुरोध ... मैं भी <5.0 समाधान जानना चाहता हूं। – HydroPowerDeveloper