2012-10-23 10 views
6

संपादित मैं मजबूर कर की उचित तरीके से इस तरह एसिंक्रोनस रूप से कार्यकर्ता आह्वान एक Task.Run साथ है, के लिए इंतजार है लगता है:Async/प्रगति/रद्दीकरण के लंबे समय से चल एपीआई तरीकों के लिए इंतजार


await Task.Run(() => builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress))); 

http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx से कुछ प्रकाश मिला।


यह आसान होना चाहिए, लेकिन मैं async/await के लिए नया हूं इसलिए मेरे साथ सहन करें। मैं कुछ लंबे समय से चल रहे संचालन के साथ एक एपीआई उजागर करने वाली कक्षा पुस्तकालय का निर्माण कर रहा हूं।


public void DoSomething(object sender, DoWorkEventArgs e) 
{ 
      BackgroundWorker bw = (BackgroundWorker)sender; 
      // e.Argument is any object as passed by consumer via RunWorkerAsync... 

      do 
      { 
       // ... do something ... 

       // abort if requested 
       if (bw.CancellationPending) 
       { 
        e.Cancel = true; 
        break; 
       } //eif 

       // notify progress 
       bw.ReportProgress(nPercent); 
      } 
} 

और ग्राहक कोड की तरह था:


BackgroundWorker worker = new BackgroundWorker 
{ WorkerReportsProgress = true, 
    WorkerSupportsCancellation = true 
}; 
worker.DoWork += new DoWorkEventHandler(_myWorkerClass.DoSomething); 
worker.ProgressChanged += WorkerProgressChanged; 
worker.RunWorkerCompleted += WorkerCompleted; 
worker.RunWorkerAsync(someparam); 

अब मैं करना चाहते हैं अतीत में, मैं प्रगति इस सरलीकृत कोड टुकड़ा में रिपोर्टिंग और निरस्तीकरण, की तरह से निपटने के लिए एक BackgroundWorker इस्तेमाल किया नए एसिंक पैटर्न का लाभ उठाएं। तो, सबसे पहले यहां यह है कि मैं अपने एपीआई में एक साधारण लंबी चलने वाली विधि कैसे लिखूंगा; यहाँ मैं सिर्फ सिर्फ एक वास्तविक दुनिया प्रक्रिया अनुकरण करने के लिए जहाँ मैं कुछ प्रसंस्करण के साथ एक फ़ाइल स्वरूप परिवर्तित करना होगा लाइन द्वारा एक फ़ाइल लाइन पढ़ रहा हूँ,:


public async Task DoSomething(string sInputFileName, CancellationToken? cancel, IProgress progress) 
{ 
    using (StreamReader reader = new StreamReader(sInputFileName)) 
    { 
     int nLine = 0; 
     int nTotalLines = CountLines(sInputFileName); 

     while ((sLine = reader.ReadLine()) != null) 
     { 
      nLine++; 
      // do something here... 
     if ((cancel.HasValue) && (cancel.Value.IsCancellationRequested)) break; 
     if (progress != null) progress.Report(nLine * 100/nTotalLines); 
     } 
     return nLine; 
    } 
} 

इस नमूने के लिए, यह कहना एक डमीवर्कर वर्ग की एक विधि है।


private void ReportProgress(int n) 
{ 
    Dispatcher.BeginInvoke((Action)(() => { _progress.Value = n; })); 
} 

private async void OnDoSomethingClick(object sender, RoutedEventArgs e) 
{ 
    OpenFileDialog dlg = new OpenFileDialog { Filter = "Text Files (*.txt)|*.txt" }; 
    if (dlg.ShowDialog() == false) return; 

     // show the job progress UI... 

    CancellationTokenSource cts = new CancellationTokenSource(); 
    DummyWorker worker = new DummyWorker(); 
    await builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress)); 

    // hide the progress UI... 
} 

IProgress इंटरफेस के लिए कार्यान्वयन, http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html से आता है तो आप उस URL का उल्लेख कर सकते: अब, यहाँ मेरे मुवक्किल कोड (एक WPF परीक्षण ऐप) है। वैसे भी, इस उपयोग परीक्षण में यूआई प्रभावी ढंग से अवरुद्ध है और मुझे कोई प्रगति दिखाई नहीं दे रही है। तो खपत कोड के संदर्भ में, इस तरह के एक परिदृश्य के लिए पूरी तस्वीर क्या होगी?

+0

हाँ, 'कार्य। रुन()' ऐसा करने का तरीका है। या संभवत: 'LongRunning' विकल्प सेट के साथ 'कार्य। फैक्टरी। स्टार्टन्यू()', यदि कार्रवाई वास्तव में लंबी है। – svick

उत्तर

9

जैसा कि ब्लॉग पोस्ट के शीर्ष पर उल्लेख किया गया है, उस पोस्ट की जानकारी पुरानी है। आपको .NET 4.5 में प्रदान किए गए नए IProgress<T> API का उपयोग करना चाहिए।

आप आई/ओ, तो बनाने के अपने मूल विधि अवरुद्ध अवरुद्ध उपयोग कर रहे हैं:

public void Build(string sInputFileName, CancellationToken cancel, IProgress<int> progress) 
{ 
    using (StreamReader reader = new StreamReader(sInputFileName)) 
    { 
    int nLine = 0; 
    int nTotalLines = CountLines(sInputFileName); 

    while ((sLine = reader.ReadLine()) != null) 
    { 
     nLine++; 
     // do something here... 
     cancel.ThrowIfCancellationRequested(); 
     if (progress != null) progress.Report(nLine * 100/nTotalLines); 
    } 

    return nLine; 
    } 
} 

और फिर Task.Run में लपेट जब आप इसे कहते हैं:

private async void OnDoSomethingClick(object sender, RoutedEventArgs e) 
{ 
    OpenFileDialog dlg = new OpenFileDialog { Filter = "Text Files (*.txt)|*.txt" }; 
    if (dlg.ShowDialog() == false) return; 

    // show the job progress UI... 

    CancellationTokenSource cts = new CancellationTokenSource(); 
    DummyWorker worker = new DummyWorker(); 
    var progress = new Progress<int>((_, value) => { _progress.Value = value; }); 
    await Task.Run(() => builder.Build(dlg.FileName, cts.Token, progress); 

    // hide the progress UI... 
} 

वैकल्पिक रूप से, आप कर सकते थे एसिंक्रोनस एपीआई का उपयोग करने के लिए Build को फिर से लिखें और फिर इसे सीधेमें लपेटे बिना ईवेंट हैंडलर से सीधे कॉल करें।

+0

धन्यवाद, मैंने एसिंक को हटाकर और रिटर्न प्रकार को शून्य में बदलकर अपनी विधि को अवरुद्ध कर दिया है। – Naftis

+0

ध्यान दें कि आपको बिल्ड() विधि से किसी भी फॉर्म UI तत्वों तक पहुंचने की अनुमति नहीं है। –

+0

@LefterisE: प्रगति संवाददाता यही है। –

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