Каква е разликата между TPL и async/await

+4 гласа
95 прегледа
попитан 2016 юни 6 в .NET от Nikoleta.V. (4,090 точки)

Опитвам се да разбера разликата между TPL и async/await когато стане въпрос за създаването на нишки. Мисля че TPL (TaskFactory.Startnew) работи сходно сThreadPool.QueueUserWorkItem по отношение на това, че изчаква за нишка от thread pool-а. Това, разбира се, ако не използваш TaskCreationOptions.LongRunning, което създава нова нишка. 

Мислех, че async/await ще работи сходно и че 

TPL: 

Factory.StartNew( () => DoSomeAsyncWork() ) 

.ContinueWith(  

    (antecedent) => { 

        DoSomeWorkAfter();  

    },TaskScheduler.FromCurrentSynchronizationContext()); 

Async/await: 

await DoSomeAsyncWork();   

DoSomeWorkAfter(); 

ще са едно и също. Доколкото прочетох изглежда, че async/await само „понякога“ създава нова нишка. Та кога създава и кога не създава нова нишка? Ако се работи с IO completion ports не трябва да се създава нишка, но иначе мисля че трябва. Предполагам че не съм напълно наясно с FromCurrentSynchronizationContext. Все си мислех, че е нишката на потребителския интерфейс. 

1 отговор

+1 глас
отговорени 2016 юни 7 от Viktor.Ivanov. (1,550 точки)

Мисля че TPL (TaskFactory.Startnew) работи сходно сThreadPool.QueueUserWorkItem по отношение на това, че изчаква за нишка от thread pool-а. 

Общо взето е така. 

Доколкото прочетох изглежда, че async/await само „понякога“ създава нова нишка. 

Всъщност, никога него прави. Ако искаш да ползваш много нишки, трябва сам да си го напишеш. Има метод Task.Run, който се ползва вместо Task.Factory.StartNew, и е вероятно най-честия начин за пускане на задача от thread pool-а. 

Ако се работи с IO completion ports не трябва да се създава нишка, но иначе мисля че трябва.  

Методи като Stream.ReadAsync ще създадат Task обвивка около IOCP (Stream-а има IOCP). 

Можеш да създадеш не- I/O, не процесорни „задачи“. Прост пример е Task.Delay, който връща задача, която завършва след определено време. 

Готиното на async/await е,че можеш да наредиш на опашка работа за thread pool-a(като Task.Run), да свършиш операции обвързани с I/O(като Stream.ReadAsync), и някаква друга операция (Task.Delay) и всички те са Task-ове!. Могат да се изчакват или да се ползват в комбинации като Task.WhenAll. 

Всеки метод, който връща Task, може да се await-ва- не е задължително да е асинхронен. Task.Delay и операции,обвързани с I/O просто използват TaskCompletionSource да създават и завършват задачи- единственото, което се върши от thread pool-а е реалното завършване на задачата, когато се хвърли event-а. 

Предполагам че не съм напълно наясно с FromCurrentSynchronizationContext. Все си мислех, че е нишката на потребителския интерфейс. 

В повечето случаи SynchronizationContext.Current е в контекста на: 

  • UI, ако текущата нишка е за UI 
  • ASP.NET заявка, ако текущата нишка обслужва ASP.NET заявка 
  • thread pool в останалите случаи 

Всяка нишка може да си зададе SynchronizationContext,така че може да има изключения. 

Забележи, че Task awaiter-а по подразбиране ще зададе оставащото от async метода на текущия SynchronizationContext(ако не е null), иначе отива в текущия TaskScheduler. Това не е толкова важно засега, но за напред ще е важно различие. 

Относно async/await: 

http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx   

Относно „конкурентност“ срещу „multithreading“. Бих казал, че async позволява конкурентност, която може да не е многонишкова. Лесно се ползва await Task.WhenAll или await Task.WhenAny за конкурентна обработка, и освен ако не използваш изрично thread pool-а(Task.Run или ConfigureAwait(false)), тогава ще имаш много конкурентни операции по едно и също време. В този случай се ползва терминът „еднонишкова конкуренция“, въпреки че в ASP.NET се оказваш с „безнишкова конкуренция“, което е готино. 

...