2013-09-25 18 views
9

Muszę wywołać wywołanie zwrotne, gdy pętla foreach zakończy wyszukiwanie wszystkich elementów w numerze List<>.Pożarowe wywołanie zwrotne po asynchronizacji Metoda zadania

private async void startSearchBtn_Click(object sender, EventArgs e) 
{ 
    await Search(files, selectTxcDirectory.SelectedPath, status); 
} 

private static async Task Search(List<string> files, string path, Label statusText) 
{ 
    foreach (string file in files) 
    { 
     XmlDocument xmlDoc = new XmlDocument(); 
     xmlDoc.Load(file); 

     statusText.Text = "Started scanning..."; 
     using (XmlReader reader = XmlReader.Create(new StringReader(xmlDoc.InnerXml), new XmlReaderSettings() { Async = true })) 
     { 
      while (await reader.ReadAsync()) 
      { 
       if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "LineName")) 
       { 
        Console.WriteLine(reader.ReadInnerXml()); 
       } 
      } 
     } 
    } 
} 

Czy jest to możliwe, a jeśli tak, jak można to zrobić?

+2

Dlaczego nie przekazać delegata jako parametr i podniesienie go w 'foreach' pętli, gdzie trzeba? Czy czegoś brakuje? –

+0

http://msdn.microsoft.com/it-it/library/system.asynccallback.aspx – Saturnix

+0

@SriramSakthivel Nie brakuje niczego, ponieważ nie wiem, co przekazuje delegata jako param i wywołując go w pętla foreach to 8-) Czy możesz napisać to jako odpowiedź? – jskidd3

Odpowiedz

16

Jest to bardzo proste, wystarczy przekazać metodę jako delegata w parametrze. następnie wywołaj go tam, gdzie chcesz.

private async void startSearchBtn_Click(object sender, EventArgs e) 
{ 
    await Search(files, selectTxcDirectory.SelectedPath, status, SearchCompleted); // <-- pass the callback method here 
} 

private static async Task Search(List<string> files, string path, Label statusText, Action<string> callback) 
{ 
    foreach (string file in files) 
    { 
     XmlDocument xmlDoc = new XmlDocument(); 
     xmlDoc.Load(file); 

     statusText.Text = "Started scanning..."; 
     using (XmlReader reader = XmlReader.Create(new StringReader(xmlDoc.InnerXml), new XmlReaderSettings() { Async = true })) 
     { 
      while (await reader.ReadAsync()) 
      { 
       if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "LineName")) 
       { 
        Console.WriteLine(reader.ReadInnerXml()); 
       } 
      } 
     } 

     // Here you're done with the file so invoke the callback that's it. 
     callback(file); // pass which file is finished 
    } 
} 

private static void SearchCompleted(string file) 
{ 
    // This method will be called whenever a file is processed. 
} 
1

Ponieważ używasz await Twój kod w startSearchBtn_Click nie będzie kontynuowana aż Search jest zakończona.

Wszystko czego potrzebujesz to coś takiego:

private async void startSearchBtn_Click(object sender, EventArgs e) 
{ 
    await Search(files, selectTxcDirectory.SelectedPath, status); 
    // run your callback here 
} 
2

bym kodować to jak poniżej. W ten sposób nadal śledzisz oczekujące zadanie (_pendingSearch), a startSearchBtn_Click pozostaje synchroniczne.

Powinieneś śledzić bieżące zadania (i móc je anulować). W przeciwnym razie użytkownik może kliknąć dwa razy z rzędu startSearchBtn i odrodzić dwa zadania wyszukiwania. To nadal może być prawidłowy scenariusz w twoim przypadku, ale zazwyczaj tak nie jest.

Task _pendingSearch = null; 
private void startSearchBtn_Click(object sender, EventArgs e) 
{ 
    // check if is _pendingSearch != null and still pending here 

    _pendingSearch = Search(files, 
     selectTxcDirectory.SelectedPath, status).ContinueWith((finishedTask) => 
    { 
     // Place your completion callback code here 
    }, TaskScheduler.FromCurrentSynchronizationContext); 
} 

private static async Task Search(List<string> files, string path, Label statusText) 
{ 
    // ... 
} 

[edited] Korzystanie await:

Task _pendingSearch = null; 
private async void startSearchBtn_Click(object sender, EventArgs e) 
{ 
    // check if is _pendingSearch != null and still pending here 

    _pendingSearch = Search(files, selectTxcDirectory.SelectedPath, status); 
    await _pendingSearch; 
    // Place your completion callback code here 
} 
+0

Jak to jest lepsze niż przy użyciu 'czekaj'? – svick

+1

@ Sick, moim celem jest nie używać podejścia polegającego na zapominaniu o ogniu, gdy użytkownik może kliknąć ten przycisk kilka razy. Moją osobistą preferencją jest unikanie programów do obsługi zdarzeń "async void", aby uniknąć pożaru i zapomnienia. Mimo to możliwe jest śledzenie oczekującego zadania w metodzie "async void", zaktualizowałem kod, aby to pokazać. – Noseratio

+1

Z wyjątkiem dwóch fragmentów kodu zrób coś innego. W twoim przykładzie 'ContinueWith()' _pendingSearch' reprezentuje kontynuację. W próbce 'await' jest to' Search() '' Task'. – svick

Powiązane problemy