Хто-небудь може пояснити, що awaitробить функція?
Хто-небудь може пояснити, що awaitробить функція?
Відповіді:
Вони щойно говорили про це в PDC вчора!
Await використовується разом із Завданнями (паралельне програмування) у .NET. Це ключове слово вводиться в наступній версії .NET. Це більш-менш дозволяє "призупинити" виконання методу, щоб дочекатися завершення Завдання. Ось короткий приклад:
//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();
В основному, ключові слова asyncта awaitдозволяють вказати, що виконання методу повинно зупинятися при будь-якому використанні await, яке позначає виклики асинхронних методів, а потім відновлюватися після завершення асинхронної операції. Це дозволяє вам викликати метод у головному потоці програми та обробляти складну роботу асинхронно, без необхідності чітко визначати потоки та об'єднання або блокувати основний потік програми.
Подумайте про це як про щось подібне до yield returnтвердження у методі, що створює IEnumerable. Коли час виконання потрапляє в yield, це в основному зберігає поточний стан методу і повертає отримане значення або посилання. Наступного разу, коли IEnumerator.MoveNext () буде викликаний для об'єкта return (який генерується внутрішньо середовищем виконання), старий стан методу відновлюється в стеку, і виконання продовжується з наступним рядком після, yield returnяк ніби ми ніколи не залишали метод. Без цього ключового слова тип IEnumerator повинен бути визначений на замовлення для зберігання стану та обробки запитів на ітерацію з методами, які дійсно можуть стати ДУЖЕ складними.
Аналогічно, метод, позначений як, asyncповинен мати принаймні один await. У цей awaitчас середовище виконання збереже стан поточного потоку та стек викликів, зробить асинхронний виклик та повернеться назад до циклу повідомлень середовища виконання, щоб обробити наступне повідомлення та забезпечити відповідь програми. Коли асинхронна операція завершена, при наступній можливості планування стек викликів для збільшення асинхронної операції повертається назад і продовжується так, ніби виклик був синхронним.
Отже, ці два нові ключові слова в основному спрощують кодування асинхронних процесів, подібно до yield returnспрощеного генерування користувацьких перерахувань. Маючи пару ключових слів та трохи попередніх знань, ви можете пропустити всі заплутані та часто схильні до помилок деталі традиційного асинхронного шаблону. Це буде НЕОЦІНОВНО майже в будь-якому графічному інтерфейсі, керованому подіями, як Winforms, WPF Silverlight.
Наразі прийнята відповідь вводить в оману.
awaitнічого не робить на паузі. Перш за все, його можна використовувати лише в методах або лямбдах, позначених як asyncі повертаються, Taskабо voidякщо вам все одно, що Taskекземпляр працює в цьому методі.
Ось ілюстрація:
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()");
}
}
Вихід:
Увійшов у DoWork (). Сплячий 3
асинхронної завдання ітерація 0
Статус завдання: WaitingForActivation
Очікування ENTER
асинхронної завдання ітерація 1
асинхронної завдання ітерація 2
асинхронної завдання ітерація 3
асинхронної завдання ітерація 4
асинхронної завдання ітерація 5
асинхронної завдання ітерація 6
асинхронної завдання ітерація 7
асинхронної завдання ітерація 8
асинхронної завдання ітерація 9
Виходу Робити роботу()
awaitвключити? Значить, якщо це викликається з потоку інтерфейсу користувача, це блокуватиме інтерфейс інтерфейсу на 3 секунди? Ідея цієї моделі полягає в тому, щоб уникати подібних дій.
Для тих, хто новачок в асинхронному програмуванні в .NET, ось (абсолютно фальшива) аналогія у сценарії, який вам може бути більш знайомий - виклики AJAX за допомогою JavaScript / jQuery. Простий допис jQuery AJAX виглядає так:
$.post(url, values, function(data) {
// AJAX call completed, do something with returned data here
});
Причиною того, що ми обробляємо результати у функції зворотного виклику, є те, що ми не блокуємо поточний потік, очікуючи повернення виклику AJAX. Тільки тоді, коли відповідь буде готова, зворотний виклик спрацює, звільняючи поточний потік робити інші речі за цей час.
Тепер, якщо JavaScript підтримував awaitключове слово (що, звичайно, ще ( поки що! )), Ви могли б досягти того ж за допомогою цього:
var data = await $.post(url, values);
// AJAX call completed, do something with returned data here
Це набагато чистіше, але, схоже, ми ввели синхронний блокуючий код. Але (підроблений) компілятор JavaScript взяв би все після awaitі підключив це до зворотного виклику, тому під час виконання другий приклад поводився б так само, як і перший.
Можливо, це не здається, що це економить вам багато роботи, але коли справа доходить до таких речей, як обробка винятків та контексти синхронізації, компілятор насправді робить для вас дуже важку роботу. Щоб отримати більше, я б порекомендував поширені запитання, а потім серію блогів Стівена Клірі .
Якби мені довелося реалізувати це на Java, це виглядало б приблизно так:
/**
* @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();
}
Ваша програма використовувала б його так:
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");
}
}
}