2016-05-20 7 views
0

ПРЕДУПРЕЖДЕНИЕ Я полный новичок с async/await, и поэтому, наверное, это неправильно понимаю!Почему мой асинхронный код не работает async?

Я пытаюсь понять, как работает этот материал, и попробовал простой бит кода в окне WPF. Я добавил обработчик события нажатия кнопки, и добавлены некоторые синхронизации и асинхронные методы следующим образом ...

public partial class MainWindow { 
    private Random _r = new Random(DateTime.Now.Millisecond); 

    public MainWindow() { 
    InitializeComponent(); 
    } 

    private async void Bleah_Click(object sender, RoutedEventArgs e) { 
    LstMessages.Items.Clear(); 
    AddToMsg("Starting..."); 
    DoSyncStuff(); 
    await DoStuffAsync(); 
    DoMoreStuffSync(); 
    AddToMsg("Done"); 
    } 

    private void DoSyncStuff() { 
    int delay = _r.Next(500, 1500); 
    AddToMsg("DoSyncStuff - waiting for " + delay + "ms"); 
    Thread.Sleep(delay); 
    AddToMsg("DoSyncStuff - finished"); 
    } 

    private void DoMoreStuffSync() { 
    int delay = _r.Next(500, 1500); 
    AddToMsg("DoMoreStuffSync - waiting for " + delay + "ms"); 
    Thread.Sleep(delay); 
    AddToMsg("DoMoreStuffSync - finished"); 
    } 

    private async Task DoStuffAsync() { 
    await Task.Run(() => { 
     int delay = _r.Next(500, 1500); 
     AddToMsg("DoStuffAsync - waiting for " + delay + "ms"); 
     Thread.Sleep(delay); 
     AddToMsg("DoStuffAsync - finished"); 
    }); 
    } 

    private void AddToMsg(string msg) { 
    Dispatcher.BeginInvoke(
     new Action(() => { LstMessages.Items.Add(DateTime.Now.ToString("HH:mm:ss.fff") + " - " + msg); })); 
    } 

LstMessages является ListBox на окне.

Когда я нажимаю кнопку, я вижу, что три метода всегда выполняются в том порядке, который я им называю, независимо от длины каждой задержки.

Я, очевидно, не понимаю, как работает этот материал, но, прочевав несколько часов и пробовав множество вариантов кода, я не могу заставить его работать, как я ожидаю.

Пожалуйста, кто-нибудь может пояснить, что я сделал неправильно здесь?

+0

Асинхронный не распараллеливание кода он просто не блокирует вызывающий поток. – juharr

+1

Хороший ресурс https://msdn.microsoft.com/en-us/magazine/jj991977.aspx - некоторые из примеров должны говорить на ваш случай – Brian

+2

Не используйте 'Thread.Sleep' в вашем асинхронном методе. Используйте 'Task.Delay', чтобы вы фактически не блокировали поток. – mason

ответ

1

Попробуйте этот подход, кажется, что вы запускаете метод асинхронного вызова, но сразу же ожидаете его в потоке пользовательского интерфейса.

private async void Bleah_Click(object sender, RoutedEventArgs e) 
{ 
    LstMessages.Items.Clear(); 
    AddToMsg("Starting..."); 
    DoSyncStuff(); 
    Task t = DoStuffAsync(); 
    DoMoreStuffSync(); 
    await t; 
    AddToMsg("Done"); 
} 
+0

Спасибо, и вам, и Will Ray помогли мне понять это. Хотел бы я отметить оба ответа в качестве ответов. Маркировка вашей, как она появилась вначале, и ответила на мой вопрос напрямую. –

0

То, что вы видите, имеет смысл, так как вы выполняете все действия по основному потоку пользовательского интерфейса. Вам нужно либо создавать и управлять собственный объект Thread/BackgroundWorker, или представить метод к ThreadPool

BackgroundWorker

Thread

ThreadPool

Каждый подход имеет свои плюсы и минусы, которые можно найти в других ответах здесь. Дайте этим ссылкам прочитать и опробовать примеры

+0

Эти 3 подхода имеют довольно много минусов, а не просто используют async/ждут должным образом с Task.Run, см. Раздел «Темы» здесь: https://msdn.microsoft.com/en-us/library/mt674882.aspx –

+0

@ thunder2709 Спасибо за ответ, но я уже знаю, как их использовать. Я пытаюсь научиться использовать новые функции –

1

Важно то, что async and await keywords don't cause additional threads to be created. (Task.Run() МОЖЕТ переместить работу в другую нить). Так что же происходит в вашем коде?

Итак, в вашем коде первый звонок DoSyncStuff() приостанавливает основной поток. Ваш звонок до DoStuffAsync() не будет выполнен даже после завершения DoSyncStuff().

Ваш призыв к DoStuffAsync срабатывают как будто это асинхронное - но потому, что вы использовали AWAIT ключевое слово в функции вызывающего абонента «ждет DoStuffAsync()», главный управляющий потоком будет вернуться к Bleah_Click() абоненту (который для целей, не будет делать что-нибудь супер интересное). Как только DoStuffAsync() завершает, управление возвращается к Bleah_Click, и выполняется DoMoreStuffSync(), что снова приостанавливает ваш основной поток.

В зависимости от вашего вопроса: я не могу сказать вам, что вы «сделали неправильно», поскольку вы не указали желаемый результат - если вы хотите приостановить выполнение потока пользовательского интерфейса и выполнить все свои функции в перечисленные порядок, то вы сделали все правильно.

2

Все, что вам есть сделать, это падение await ключевого слова в вашем коде.

Процитирую blog post Эрик Липперт:

Всякий раз, когда задача «ждали», остаток текущего метода подписался как продолжение этой задачи, а затем управление немедленно возвращается вызывающему , Когда задача завершается, вызывается продолжение и метод запускается там, где он был раньше.

Добавляя в await ключевое слово, вы эффективно говорить «как только этот метод асинхронной завершен, вести с остальной частью этого метода».

Это может быть проще понять с помощью методов, возвращающих значение. Следующая программа сразу начнет два метода и будет await результатом метода async после вызова метода синхронизации. Вы можете попробовать перемещать линию await, чтобы посмотреть разницу в поведении.

class Program 
{ 
    static void Main(string[] args) 
    { 
     MainAsync(); 
     Console.ReadKey(); 
    } 

    static async void MainAsync() 
    { 
     var task = GetNumberAsync(); 
     var syncNumber = GetNumber(); 
     var asyncNumber = await task; // moving this line above "GetNumber();" will make these run in order 

     Console.WriteLine(syncNumber); 
     Console.WriteLine(asyncNumber); 
    } 

    private static int GetNumber() 
    { 
     Console.WriteLine("DoSomeWork - started"); 
     Thread.Sleep(1000); 
     Console.WriteLine("DoSomeWork - finished"); 
     return 11; 
    } 

    private static async Task<int> GetNumberAsync() 
    { 
     Console.WriteLine("GetNumberAsync - started"); 
     await Task.Delay(1000); 
     Console.WriteLine("GetNumberAsync - finished"); 
     return 22; 

    } 
} 
+0

Отличный ответ. Хотел бы я отметить как ваши, так и Криса в качестве ответов, поскольку он ответил на мой вопрос напрямую, а ваш дал больше информации. Пожалуйста, не обижайтесь, но я, вероятно, буду отмечать его как ответ, поскольку он пришел первым. еще раз спасибо –