Czy ktoś może wyjaśnić, co await
robi ta funkcja?
c#
c#-5.0
async-await
Chris Nicol
źródło
źródło
Odpowiedzi:
Właśnie o tym rozmawiali wczoraj w PDC !
Await jest używany w połączeniu z Tasks (programowanie równoległe) w .NET. To słowo kluczowe wprowadzane w następnej wersji .NET. Pozwala mniej więcej „wstrzymać” wykonywanie metody, aby poczekać na zakończenie wykonania zadania. Oto krótki przykład:
//create and run a new task Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation); //run some other code immediately after this task is started and running ShowLoaderControl(); StartStoryboard(); //this will actually "pause" the code execution until the task completes. It doesn't lock the thread, but rather waits for the result, similar to an async callback // please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return dataTask.Start(); DataTable table = await dataTask; //Now we can perform operations on the Task result, as if we're executing code after the async operation completed listBoxControl.DataContext = table; StopStoryboard(); HideLoaderControl();
źródło
Zasadniczo słowa kluczowe
async
iawait
pozwalają określić, że wykonanie metody powinno zatrzymać się przy każdym użyciuawait
, które oznacza wywołania metod asynchronicznych, a następnie wznowić po zakończeniu operacji asynchronicznej. Pozwala to na wywołanie metody w głównym wątku aplikacji i asynchroniczną obsługę złożonej pracy, bez konieczności jawnego definiowania wątków i sprzężeń lub blokowania głównego wątku aplikacji.Potraktuj to jako nieco podobne do
yield return
instrukcji w metodzie tworzącej IEnumerable. Kiedy środowisko wykonawcze osiągnie wartośćyield
, w zasadzie zapisze bieżący stan metody i zwróci otrzymaną wartość lub odniesienie. Następnym razem, gdy IEnumerator.MoveNext () zostanie wywołana na zwracanym obiekcie (który jest generowany wewnętrznie przez środowisko uruchomieniowe), stary stan metody jest przywracany na stos, a wykonywanie jest kontynuowane z następnym wierszem poyield return
znaku, tak jakbyśmy nigdy nie opuszczali metoda. Bez tego słowa kluczowego typ IEnumerator musi być niestandardowo zdefiniowany, aby przechowywać stan i obsługiwać żądania iteracji, przy użyciu metod, które mogą stać się BARDZO złożone.Podobnie metoda oznaczona jako
async
musi mieć co najmniej jedenawait
. Na zasadzieawait
, że środowisko wykonawcze uratuje państwowej i stosu wywołań bieżącego wątku, uczynić asynchroniczne wywołanie, i relaks z powrotem do środowiska wykonawczego w pętli komunikatów do obsługi następnej wiadomości i utrzymania aplikacji elastyczne. Po zakończeniu operacji asynchronicznej przy następnej okazji planowania stos wywołań do operacji asynchronicznej jest z powrotem wypychany i kontynuowany tak, jakby wywołanie było synchroniczne.Tak więc te dwa nowe słowa kluczowe w zasadzie upraszczają kodowanie procesów asynchronicznych, podobnie jak
yield return
upraszczają generowanie niestandardowych wyliczalnych. Dzięki kilku słowom kluczowym i odrobinie wiedzy podstawowej możesz pominąć wszystkie zagmatwane i często podatne na błędy szczegóły tradycyjnego wzorca asynchronicznego. Będzie to NIEZNACZNE w prawie każdej aplikacji GUI sterowanej zdarzeniami, takiej jak Winforms, WPF lub Silverlight.źródło
Obecnie akceptowana odpowiedź jest myląca.
await
niczego nie wstrzymuje. Przede wszystkim może być używany tylko w metodach lub lambdach oznaczonych jakoasync
i zwracającychTask
lubvoid
jeśli nie zależy ci na tym, abyTask
instancja działała w tej metodzie.Oto ilustracja:
internal class Program { private static void Main(string[] args) { var task = DoWork(); Console.WriteLine("Task status: " + task.Status); Console.WriteLine("Waiting for ENTER"); Console.ReadLine(); } private static async Task DoWork() { Console.WriteLine("Entered DoWork(). Sleeping 3"); // imitating time consuming code // in a real-world app this should be inside task, // so method returns fast Thread.Sleep(3000); await Task.Run(() => { for (int i = 0; i < 10; i++) { Console.WriteLine("async task iteration " + i); // imitating time consuming code Thread.Sleep(1000); } }); Console.WriteLine("Exiting DoWork()"); } }
Wynik:
źródło
await
? Oznacza to, że jeśli jest to wywoływane z wątku interfejsu użytkownika, zablokuje wątek interfejsu użytkownika na 3 sekundy? Ideą tego modelu jest unikanie robienia takich rzeczy.Dla każdego, kto nie ma doświadczenia w programowaniu asynchronicznym w .NET, oto (całkowicie fałszywa) analogia w scenariuszu, z którym możesz być lepiej zaznajomiony - wywołania AJAX przy użyciu JavaScript / jQuery. Prosty post jQuery AJAX wygląda następująco:
$.post(url, values, function(data) { // AJAX call completed, do something with returned data here });
Powodem, dla którego przetwarzamy wyniki w funkcji zwrotnej, jest to, że nie blokujemy bieżącego wątku podczas oczekiwania na zwrot wywołania AJAX. Dopiero gdy odpowiedź jest gotowa, wywołanie zwrotne zostanie uruchomione, zwalniając bieżący wątek do wykonywania innych czynności w międzyczasie.
Teraz, jeśli JavaScript obsługuje
await
słowo kluczowe (co oczywiście nie ma ( jeszcze! )), Możesz osiągnąć to samo w ten sposób:var data = await $.post(url, values); // AJAX call completed, do something with returned data here
To dużo czystsze, ale z pewnością wygląda na to, że wprowadziliśmy synchroniczny, blokujący kod. Ale (fałszywy) kompilator JavaScript zająłby potem wszystko
await
i to z wywołaniem zwrotnym, więc w czasie wykonywania drugi przykład zachowywałby się tak samo jak pierwszy.Może się wydawać, że nie oszczędza ci to dużo pracy, ale jeśli chodzi o takie rzeczy, jak obsługa wyjątków i konteksty synchronizacji, kompilator w rzeczywistości wykonuje dla ciebie dużo ciężkiej pracy. Aby uzyskać więcej informacji, polecam często zadawane pytania, a następnie serię blogów Stephena Cleary'ego .
źródło
Gdybym musiał to zaimplementować w Javie, wyglądałoby to mniej więcej tak:
/** * @author Ilya Gazman */ public abstract class SynchronizedTask{ private ArrayList<Runnable> listeners = new ArrayList<Runnable>(); private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000)); public final void await(Runnable listener){ synchronized (this) { listeners.add(listener); } } public void excecute(){ onExcecute(); for (int i = listeners.size() - 1; i >= 0; i--) { Runnable runnable; synchronized (this) { runnable = listeners.remove(i); } threadPoolExecutor.execute(runnable); } } protected abstract void onExcecute(); }
Twoja aplikacja użyje tego w następujący sposób:
public class Test{ private Job job = new Job(); public Test() { craeteSomeJobToRunInBackground(); methode1(); methode2(); } private void methode1(){ System.out.println("Running methode 1"); job.await(new Runnable() { @Override public void run() { System.out.println("Continue to running methode 1"); } }); } private void methode2(){ System.out.println("Running methode 2"); } private void craeteSomeJobToRunInBackground() { new Thread(new Runnable() { @Override public void run() { job.excecute(); } }).start(); } private class Job extends SynchronizedTask{ @Override protected void onExcecute() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Job is done"); } } }
źródło