Async/

2012-10-23 39 views
6

Düzenleme Ben zorlama uygun yolu bu gibi uyumsuz işçiyi çağırmak bir Task.Run ile karşı bekliyor herhalde ilerleme/iptali ile uzun süren API yöntemleri için bekliyor: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'dan biraz ışık aldım.


bu kolay olmalı, ancak uyumsuzluk/beklemede yeniyim, bu yüzden benimle kalmayın. Uzun süredir devam eden operasyonlarla bir API açığa çıkaran bir sınıf kütüphanesi yapıyorum.


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); 
      } 
} 

ve benzeri istemci kodu oldu: İsterdim Şimdi


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

Eskiden ilerleme raporlama ve iptal gibi bu basitleştirilmiş kod parçasını başa BackgroundWorker kullanılan Yeni zaman uyumsuz kalıbı. Dolayısıyla, öncelikle burada API'mda uzun süren basit bir yöntem yazacağım; Burada sadece bazı işleme ile bir dosya biçimi dönüştürme sahip olacakları sadece gerçek dünya sürecini taklit, satır bir dosya satır okuyorum: Bu örnek uğruna


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; 
    } 
} 

, bu demek bir DummyWorker sınıfının bir yöntemidir. bu URL başvurabilmeniz için


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 arayüzü için uygulama, http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html geliyor: Şimdi, burada müvekkilim kodu (WPF test uygulaması) 'dir. Her neyse, bu kullanım testinde UI etkili bir şekilde engellenir ve hiçbir ilerleme göremiyorum. Öyleyse, böyle bir senaryonun, tüketici koduyla ilgili olarak tam resmi ne olurdu?

+0

Evet, 'Task.Run()' bunu yapmanın yoludur. Ya da eylem gerçekten uzun ise, 'LongRunning' seçeneği ile Task.Factory.StartNew()'. – svick

cevap

9

Bu blog yayınının üstünde belirtildiği gibi, bu gönderideki bilgiler güncel değil. .NET 4.5'de sağlanan yeni IProgress<T> API'sini kullanmalısınız.

I/O, o zaman yapmak çekirdek yöntemi engelleme engelleme kullanıyorsanız:

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; 
    } 
} 

ve sonra onu çağırdığınızda Task.Run sarın: Alternatif

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... 
} 

, yapabilirsin Eşzamansız API'leri kullanmak için Build'u yeniden yazın ve ardından Task.Run'a sarmadan doğrudan olay işleyicisinden arayın.

+0

Teşekkürler, async'i kaldırarak ve dönüş türünü geçersiz kılarak yöntemin engellenmesini sağladım. – Naftis

+0

Build() yönteminden herhangi bir form UI öğesine erişme izniniz olmadığını unutmayın. –

+0

@LefterisE: İlerleme raportörü budur. –