2015-05-12 4 views
1

код:«асинхронный ждет», как понять его

static void Main(string[] args) 
    { 
     Test(); 

     Console.WriteLine("Main Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); 

     Console.Read(); 
    } 

    static async void Test() 
    { 
     Console.WriteLine("before wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); 

     await GetName(); 

     Console.WriteLine("after wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); 
    } 

    static async Task GetName() 
    { 
     await Task.Run(() => 
      { 
       Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); 
       Console.WriteLine("In antoher thread....."); 
      }); 
    } 

результата:

before wait Current Thread Id:9 
Current Thread Id :10 
In antoher thread..... 
Main Thread Id :9  
after wait Current Thread Id:10 or sometimes is after wait Current Thread Id:11 

я не Kown почему «Главный поток Id» работает быстрее, чем «после ожидания тока Идентификатор потока ". и почему имеет три ThreadID.

+1

[Лучшие практики в программировании асинхронного программирования] (https://msdn.microsoft.com/en-us/magazine/jj991977.aspx): «Методы асинхронного вывода, имеющие Void, имеют определенную цель: сделать асинхронные обработчики событий возможными. Чтобы обобщить это первое руководство, вы должны предпочесть async Task для async void " –

+1

Обратите внимание на то, что соглашение об именах для методов async суффиксное имя' Async', поэтому ваши методы могут быть переименованы в 'TestAsync' и' GetNameAsync'. – Default

ответ

3

Ну, вы можете легко проследить, что происходит.

Для основного потока:

  1. Метод Test называется
  2. before wait распечатывается
  3. новая задача в очереди на ThreadPool и Test немедленно возвращается обратно в Main

До этого момента это определенная синхронная последовательность. Остальная часть не является детерминированной, потому что теоретически либо остальные Main, либо threadpool Task могут выполнять в первую очередь - на самом деле они могут (теоретически) также чередовать, причем Main Thread Id распечатывается между Current thread Id и In another thread.

Однако after wait будет всегда печататься послеIn another thread.

Вы уже можете видеть, что есть не менее две темы - это не должно удивлять, когда вы используете Task.Run. Итак, откуда же появился третий? Ну, await не делает никакой магии. В IL нет инструкции для магии. Так что на самом деле это просто расписание продолжения на Task, которое вы ожидаете, с остальной частью метода. И поскольку контекст синхронизации не используется для продолжения продолжения продолжения, продолжение будет просто снова поставлено в очередь на пул потоков - с помощью другого потока. Однако теоретически продолжение может запускаться синхронно в исходном потоке (то есть в том, что используется в Task.Run), хотя в этом случае я предполагаю, что это просто повторное использование исходного потока, а не синхронное продолжение. Но вы можете быть абсолютно уверены в одном: это будет никогда не запускается в основном потоке - если вы явно не настроили контекст синхронизации и не обработали заданные задачи в основном потоке вместо использования Console.Read для окончательной операции блокировки.

1

Это потому, что Test работает асинхронно, вы не ждете результата.

Вы можете сделать это, чтобы ждать результата:

Task t = new Task(Test); 

t.Wait(); 

Кроме того, ваш метод async должен фактически вернуть экземпляр Task, поскольку void используется только для обработчиков событий асинхронных (MSDN).

0

SynchronizationContext в консольном приложении - это новый поток, поэтому async/await не будет работать так, как вы ожидаете в этой среде. Попробуйте приложение WPF.

+0

Он задается вопросом, почему у него определенное поведение, и я сказал ему. В каком-то описании всегда есть контекст синхронизации. Прочтите это: https://msdn.microsoft.com/en-us/magazine/gg598924.aspx –

+0

a SynchronizationContext - это не поток – Default

0

Давайте отрисуем ваш код!

Это один остается, как это было

static void Main(string[] args) 
{ 
    Test(); 

    Console.WriteLine("Main Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); 

    Console.Read(); 
} 

Это метод асинхронной. Более конкретно, это асинхронный метод, который является своего рода неуклюжим; вы скоро увидите разницу. Как обычный метод, это выглядит следующим образом:

static void Test() 
{ 
    Console.WriteLine("before wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); 

    Task getnameresult = GetName(); 

    Action<Task> continuation = (task) => { 
     Console.WriteLine("after wait Current Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); 
    } 

    Task continuationresult = getnameresult.ContinueWith(continuation); 
    //you are guaranteed that continuation will run on some (unspecified) thread after the taskgetnameresult completes 

    return; 

    //note that we do nothing with continuationresult, not even return it, so there is no way to know if it has completed. 
} 

И GetName становится

static Task GetName() 
{ 
    return Task.Run(() => 
     { 
      Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId); 
      Console.WriteLine("In antoher thread....."); 
     }); 
} 

методы, которые устанавливают свой asynchronity здесь Task.ContinueWith: https://msdn.microsoft.com/en-us/library/dd270696%28v=vs.110%29.aspx и Task.Run: https://msdn.microsoft.com/en-us/library/hh195051%28v=vs.110%29.aspx

Ни один из этих методов не говорит много о потоках, просто чтобы они могли запускаться несинхронно. Время выполнения может и будет планировать их по своему усмотрению. Любые знания о том, как это работает, следует рассматривать как деталь реализации, и вы должны предположить, что это может различаться при каждом запуске приложения (и это будет отличаться, как указывают некоторые другие ответы в консольном приложении или приложении WPF)

Так что здесь происходит. Ну, во-первых, Main позвонит Test(). Test сначала напечатает сообщение «before wait Current Thread Id». Затем он вызывает GetName()

Этот метод планирует действие, которое будет выполняться в каком-то неуказанном потоке в какое-то неопределенное время в будущем, которое будет печатать «Current Thread Id» и «В другом потоке .....» и возвращает задача, представляющая это действие, возвращая управление обратно на Test

Test теперь создаст новую задачу, которая будет в другое время в будущем, но определенно после возвращенной задачи напечатать сообщение «после ожидания Текущая тема ID "и обратный контроль до Main. Main теперь заблокирует поток, пока он не прочитает что-то из консоли.

Итак, давайте все вместе. Мы точно знаем, что сначала печатается «до ожидания», что является простым синхронным вызовом.

Затем мы знаем, что после этого будет напечатана строка «Текущий поток» и будет напечатана строка «Основной поток», но мы не дали никаких обещаний относительно порядка, в котором они будут выполняться, или если они могут даже работать одновременно.

В вашей ситуации среда выполнения определяет, что она выполнит задачу, которая сначала печатает строку «Текущий поток». Зачем? Причины. SynchronisationContext s. Но самое главное, вещи, которые вы не должны заботиться. Когда вы программируете асинхронно, вы отказываетесь от контроля над тем, что работает первым, и тем, что работает дальше.

Тогда то же самое происходит и для следующего продолжения. Не существует внутренней связи между тем, когда печатается «Идентификатор основного потока», и когда печатается «После ожидания», только после того, как печатается строка «Текущий поток», должна появиться только «После ожидания».

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

Смежные вопросы