SlideShare a Scribd company logo
1 of 44
Multithreading and
   Parallelism
Use cases
- Масштабованість на multi-CPU машинах
  - Виконання швидше
  - Обробка більшої кількості запитів
- Приховування затримок (hiding latency)
  - Обчислення, доки чекаємо на IO
- Відгук UI (responsiveness)
  - «Усунення» блокуючих операцій
Threads
Process
Процес визначає virtual address space.
Досягається ізоляція, оскільки
- процеси не можуть напряму адресувати
   пам’ять інших процесів.
Разом з тим, спільне використання:
- спільна бібліотека може входити до
адресного простору кількох процесів.
Процес не виконує код,
а лише надає ресурси і контекст для
        виконання потоків
Потоки (threads) виконують код
Потоки виконуються в межах процесу
Мають доступ до всього адресного простору
процесу.
Кожен потік має свій стек та значення
регістрів CPU
Windows
- Багатозадачна ОС
- Витісняюче планування на основі пріоритетів
  - (preemptive scheduling, priority-based)
- Потік виконується на будь-якому CPU
- CPU завжди виконує той потік, який має
  найвищий пріоритет (і який готовий до
  виконання)
Планування (scheduling)
- Потік виконується протягом сталого періоду часу
  – кванту
- Величина квантів може бути різна, залежить від:
  - Конфіга системи (короткі, довгі)
  - Статуса процесу (активний, фоновий)
- По закінченню кванта ОС перевіряє, чи є готовий
  потік з таким же пріоритетом
- Як тільки з’являється потік з вищим пріоритетом,
  поточний потік  витісняється
  - Навіть якщо його квант не закінчився
Переключення контексту
(context switching)
Вибравши новий потік для виконання, ОС
переключає контекст.
Зберігається стан регістрів CPU + IP
попереднього потоку.
Відновлюються регістри CPU нового потоку.
Потік звільняє CPU, коли:
- Переходить в стан очікування самостійно
  - Диск, мережа, пам’ять (!), синхронізація
- Витісняється потоком з вищим пріоритетом
  - Який закінчив щось очікувати (диск, мережа,...)
  - Його пріоритет збільшився
- Завершився квант
- Завершився потік
Динамічне підвищення
           пріоритету
- Після закінчення вводу/виводу
- Після закінчення очікування
- При пробудженні GUI потоків через
  операції з вікнами
- Готовий до виконання потік довший час не
  має шансу виконатись
Також при цьому може збільшуватись квант.
Вибір CPU для потоку
- Вибирається процесор, який простоює
- Якщо немає вільного процесора –
  витіснення на ідеальному процесорі
Scheduling granularity
Windows планує потоки, не процеси

Наслідок:
  Процес А має 1 потік
  Процес Б має 9 потоків
  Процес Б отримає 90% процесорного часу
  (за умови однакового пріоритету потоків)
Threads in .NET
- Створення потоку:
  - new Thread(Action)
- Запуск:
  - Thread.Start()
- Очікування доки потік завершиться:
  - Thread.Join()
Thread pool
- Пул потоків виконує завдання
  використовуючи вже створений набір
  потоків
- Додання нового завдання:
  - ThreadPool.QueueUserWorkItem(delegate)
UI message loop
- Один GUI потік обробляє повідомлення з черги:
  - Clicks, repaints, mouse moves, key presses, …
- Події обробляються послідовно
- Отже, якщо одна подія займає довгий час, решта
  будуть чекати
- Отже, довготривалі події ніколи не повинні
  виконуватись в UI потоці
  - IO, важкі обчислення
Оновлення контролів
- Щоб змінити контрол, код повинен
  виконуватись в UI потоці
- Інші потоки можуть програмно додати
  повідомлення в чергу UI потоку
Demo
Task Parallel Library (TPL)
Since .NET 4.0
http://msdn.microsoft.com/en-us/library/dd460717.aspx
MSDN Parallel Computing Dev Center
Task
        http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx


Абстрагує значення, яке буде* доступне в
майбутньому.

Значення є результатом
- Обчислення (CPU)
- Вводу (IO)

Task є .NET реалізацією future and promise
Значення буде* доступне
- Можливо вже є доступне
  - Таск завершився до звернення за результатом,
  - Також таск міг виконатись в цьому ж потоці
- Можливо не буде, бо станеться помилка
- Або обчислення буде відмінено
Створення і запуск
Task Task.Factory.StartNew(Action);
Task<TResult> Task.Factory.StartNew(Func<TResult>);

Або Task constructor + Task.Start() method
Виконання
Task – не потік.
За замовчуванням таски виконуються
потоками з thread pool
Наслідок 1: таск може почати виконуватись не
одразу
Наслідок 2: довготривалий таск буде займати потік
з thread pool
(цей потік не зможе працювати над рештою задач)
Виконання довготривалого Task`а
Буде створено окремий потік

Task.Factory.StartNew(() =>
    { …
    },
    TaskCreationOptions.LongRunning);
Завершення, результат
- Task.Wait() // with optional timeout
- Task.Result

Потік заблокується, поки таск не
завершиться
Очікування декількох тасків
Task.WaitAll(Task[]) // with optional timeout
Task.WaitAny(Task[])
Demo
Handle exceptions option 1
Task<int> t = Task.Factory.StartNew(() =>
    {
        ...
        throw new InvalidOperationException("error");
        return 42;
    });
                              Тут виникне Exception
int result = t.Result;
                               AggregateException


AggregateException.InnerException(s) міститиме
початковий exception
Handle exceptions option 2
Check Task state:
- Task.IsCompleted
- Task.IsFaulted
- Task.Exception     AggregateException

- Task.Status
Unhandled exceptions
Якщо не обробити exception, TPL його перевикине
при фіналізації Task’a

Викличеться подія
TaskScheduler.UnobservedTaskException


Помилка вважається обробленою при:
- Виклику Task.Result, Task.Wait() etc.
- Звертанні до Task.Exception
Demo
Continuations
Задача: після завершення таска виконати
наступний.
В наступному таску – доступний результат
попереднього.

Task<U> Task<T>.ContinueWith(antecedent => {...});


TaskFactory.ContinueWhenAll(Task[], tasks => {...});
Continuations, способи
-   One-to-one
-   Many-to-one
-   One-to-many
-   Chain
Demo
Деякі приклади використання Tasks
 - Досягнення паралелізму
   - Виконання CPU-bound задач паралельно
   - Виконання IO-bound задач паралельно
 - Responsive UI

                    Demo
Синхронізація
Data race
int count = 0;
for (int i = 0; i < data.Length; i++)
{
    var task = Task.Factory.StartNew(() =>
        {
            count++;     Not atomic!
        });
    tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());
Preemption
      Thread A
reg = read count (1)             Thread B
                          reg = read count (1)

                          inc reg (2)

                          write reg to count (2)
inc reg (2)

write reg to count (2)
Рішення 1 – interlocked
int count = 0;
for (int i = 0; i < data.Length; i++)
{
    var task = Task.Factory.StartNew(() =>
        {
                                                   Atomic,
            Interlocked.Increment(ref count);
                                                Flushes caches
        });
    tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());
Рішення 2 – locking (critical section)
int count = 0;
object syncRoot = new object();
for (int i = 0; i < data.Length; i++)
{
    var task = Task.Factory.StartNew(() =>
        {
            lock (syncRoot)       Other threads
            {                     cannot acquire
                                     held lock
                count++;
            }
        });
    tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());
Parallel Loops
Parallel LINQ
Parallel.For() and ForEach()
Заблокується, доки всі ітерації не виконаються

 Parallel.For(0, data.Length, i =>
     {                                           Є можливість зробити
                                                 Break, передавши state
         ProcessItem(data, i);
     });

 Parallel.ForEach(data, value =>
     {
         ProcessItem(value);
     });
Parallel LINQ (PLINQ)
var result =
    from value in data.AsParallel()
    where value % 2 == 0
    let cube = Math.Pow(value, 3)
    let processed = ProcessItem(value)
    select new { Cube = cube, Result = processed };

parallelResult.ToArray();

                  Виконання почнеться тут
Варто також дізнатись про:
- Task.Status, Task lifecycle
    http://blogs.msdn.com/b/pfxteam/archive/2009/08/30/9889070.aspx

-   Continuations http://msdn.microsoft.com/en-us/library/dd235663
-   TaskSchedulers http://msdn.microsoft.com/en-us/library/dd997402
-   Cancellation http://msdn.microsoft.com/en-us/library/dd997364.aspx
-   Synchronization primitives http://msdn.microsoft.com/en-
    us/library/ms228964.aspx

- Concurrent collections http://msdn.microsoft.com/en-
    us/library/system.collections.concurrent.aspx

- Безплатна книжка, ще одна

More Related Content

What's hot (12)

Лекція №11
Лекція №11Лекція №11
Лекція №11
 
System programing module 2
System programing module 2System programing module 2
System programing module 2
 
5 Підсистема введення/виведення. OPC
5 Підсистема введення/виведення. OPC5 Підсистема введення/виведення. OPC
5 Підсистема введення/виведення. OPC
 
System programing module 3
System programing module 3System programing module 3
System programing module 3
 
Лр1 вірт машина
Лр1 вірт машинаЛр1 вірт машина
Лр1 вірт машина
 
лр7 ethernet
лр7 ethernet лр7 ethernet
лр7 ethernet
 
Інші підсистеми
Інші підсистемиІнші підсистеми
Інші підсистеми
 
лр5 основи modbus tcp
лр5 основи modbus tcpлр5 основи modbus tcp
лр5 основи modbus tcp
 
лр3 основи modbus
лр3 основи modbusлр3 основи modbus
лр3 основи modbus
 
Короткий опис лабораторного практикуму по MOM
Короткий опис лабораторного практикуму по MOMКороткий опис лабораторного практикуму по MOM
Короткий опис лабораторного практикуму по MOM
 
System programing module 1
System programing module 1System programing module 1
System programing module 1
 
Лекція №10
Лекція №10Лекція №10
Лекція №10
 

Viewers also liked (6)

ASP.Net part 2
ASP.Net part 2ASP.Net part 2
ASP.Net part 2
 
ASP.Net MVC
ASP.Net MVCASP.Net MVC
ASP.Net MVC
 
cpp-2013 #7 Templates and STL Containers
cpp-2013 #7 Templates and STL Containerscpp-2013 #7 Templates and STL Containers
cpp-2013 #7 Templates and STL Containers
 
Mobile applications development
Mobile applications developmentMobile applications development
Mobile applications development
 
cpp-2013 #15 Databases
cpp-2013 #15 Databasescpp-2013 #15 Databases
cpp-2013 #15 Databases
 
cpp-2013 #18 Qt Part 2
cpp-2013 #18 Qt Part 2cpp-2013 #18 Qt Part 2
cpp-2013 #18 Qt Part 2
 

Similar to Multithreading and parallelism

System programing module 2. Threads
System programing module 2. ThreadsSystem programing module 2. Threads
System programing module 2. ThreadsAndrii Hladkyi
 
Lec16 промiжне програмне забезпечення
Lec16 промiжне програмне забезпеченняLec16 промiжне програмне забезпечення
Lec16 промiжне програмне забезпеченняcit-cit
 
iPhone Objective-C Development (ukr) (2009)
iPhone Objective-C Development (ukr) (2009)iPhone Objective-C Development (ukr) (2009)
iPhone Objective-C Development (ukr) (2009)Anatoliy Okhotnikov
 
DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...
DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...
DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...DevOps_Fest
 
01 c# basics
01 c# basics01 c# basics
01 c# basicseleksdev
 
[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)
[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)
[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)Exoft LLC
 
Загальні принципи розроблення АРМ оператора на базі SCADA/HMI
Загальні принципи розроблення АРМ оператора на базі SCADA/HMIЗагальні принципи розроблення АРМ оператора на базі SCADA/HMI
Загальні принципи розроблення АРМ оператора на базі SCADA/HMIПупена Александр
 

Similar to Multithreading and parallelism (20)

System programing module 2. Threads
System programing module 2. ThreadsSystem programing module 2. Threads
System programing module 2. Threads
 
L l13
L l13L l13
L l13
 
Lec16 промiжне програмне забезпечення
Lec16 промiжне програмне забезпеченняLec16 промiжне програмне забезпечення
Lec16 промiжне програмне забезпечення
 
iPhone Objective-C Development (ukr) (2009)
iPhone Objective-C Development (ukr) (2009)iPhone Objective-C Development (ukr) (2009)
iPhone Objective-C Development (ukr) (2009)
 
tsql
tsqltsql
tsql
 
Design patterns part 1
Design patterns part 1Design patterns part 1
Design patterns part 1
 
Функції в C++
Функції в C++Функції в C++
Функції в C++
 
Rpc
RpcRpc
Rpc
 
DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...
DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...
DevOps Fest 2020. Володимир Мельник. TuchaKube - перша українська DevOps/Host...
 
базовI структури алгоритму урок 4
базовI структури алгоритму урок 4базовI структури алгоритму урок 4
базовI структури алгоритму урок 4
 
Automated testing
Automated testingAutomated testing
Automated testing
 
Design patterns part 2
Design patterns part 2Design patterns part 2
Design patterns part 2
 
Tdd, ти де?
Tdd, ти де?Tdd, ти де?
Tdd, ти де?
 
01 c# basics
01 c# basics01 c# basics
01 c# basics
 
Coding for Future in Lutsk. JavaScript. Part 5
Coding for Future in Lutsk. JavaScript. Part 5Coding for Future in Lutsk. JavaScript. Part 5
Coding for Future in Lutsk. JavaScript. Part 5
 
ASP.Net basics
ASP.Net basics ASP.Net basics
ASP.Net basics
 
[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)
[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)
[Knowledge Sharing] - Behavioral patterns by Pavlo Serdyuk (UKR)
 
Clean code (UA)
Clean code (UA)Clean code (UA)
Clean code (UA)
 
Загальні принципи розроблення АРМ оператора на базі SCADA/HMI
Загальні принципи розроблення АРМ оператора на базі SCADA/HMIЗагальні принципи розроблення АРМ оператора на базі SCADA/HMI
Загальні принципи розроблення АРМ оператора на базі SCADA/HMI
 
Theme20_ajax
Theme20_ajaxTheme20_ajax
Theme20_ajax
 

More from Victor Matyushevskyy

More from Victor Matyushevskyy (14)

Service oriented programming
Service oriented programmingService oriented programming
Service oriented programming
 
Java script + extjs
Java script + extjsJava script + extjs
Java script + extjs
 
Основи Баз даних та MS SQL Server
Основи Баз даних та MS SQL ServerОснови Баз даних та MS SQL Server
Основи Баз даних та MS SQL Server
 
Usability
UsabilityUsability
Usability
 
Windows forms
Windows formsWindows forms
Windows forms
 
Practices
PracticesPractices
Practices
 
06.1 .Net memory management
06.1 .Net memory management06.1 .Net memory management
06.1 .Net memory management
 
06 LINQ
06 LINQ06 LINQ
06 LINQ
 
05 functional programming
05 functional programming05 functional programming
05 functional programming
 
04 standard class library c#
04 standard class library c#04 standard class library c#
04 standard class library c#
 
#3 Об'єктно орієнтоване програмування (ч. 2)
#3 Об'єктно орієнтоване програмування (ч. 2)#3 Об'єктно орієнтоване програмування (ч. 2)
#3 Об'єктно орієнтоване програмування (ч. 2)
 
#2 Об'єктно орієнтоване програмування (ч. 1)
#2 Об'єктно орієнтоване програмування (ч. 1)#2 Об'єктно орієнтоване програмування (ч. 1)
#2 Об'єктно орієнтоване програмування (ч. 1)
 
#1 C# basics
#1 C# basics#1 C# basics
#1 C# basics
 
#0 Вступна лекція
#0 Вступна лекція#0 Вступна лекція
#0 Вступна лекція
 

Multithreading and parallelism

  • 1. Multithreading and Parallelism
  • 2. Use cases - Масштабованість на multi-CPU машинах - Виконання швидше - Обробка більшої кількості запитів - Приховування затримок (hiding latency) - Обчислення, доки чекаємо на IO - Відгук UI (responsiveness) - «Усунення» блокуючих операцій
  • 4. Process Процес визначає virtual address space. Досягається ізоляція, оскільки - процеси не можуть напряму адресувати пам’ять інших процесів. Разом з тим, спільне використання: - спільна бібліотека може входити до адресного простору кількох процесів.
  • 5. Процес не виконує код, а лише надає ресурси і контекст для виконання потоків
  • 6. Потоки (threads) виконують код Потоки виконуються в межах процесу Мають доступ до всього адресного простору процесу. Кожен потік має свій стек та значення регістрів CPU
  • 7. Windows - Багатозадачна ОС - Витісняюче планування на основі пріоритетів - (preemptive scheduling, priority-based) - Потік виконується на будь-якому CPU - CPU завжди виконує той потік, який має найвищий пріоритет (і який готовий до виконання)
  • 8. Планування (scheduling) - Потік виконується протягом сталого періоду часу – кванту - Величина квантів може бути різна, залежить від: - Конфіга системи (короткі, довгі) - Статуса процесу (активний, фоновий) - По закінченню кванта ОС перевіряє, чи є готовий потік з таким же пріоритетом - Як тільки з’являється потік з вищим пріоритетом, поточний потік  витісняється - Навіть якщо його квант не закінчився
  • 9. Переключення контексту (context switching) Вибравши новий потік для виконання, ОС переключає контекст. Зберігається стан регістрів CPU + IP попереднього потоку. Відновлюються регістри CPU нового потоку.
  • 10. Потік звільняє CPU, коли: - Переходить в стан очікування самостійно - Диск, мережа, пам’ять (!), синхронізація - Витісняється потоком з вищим пріоритетом - Який закінчив щось очікувати (диск, мережа,...) - Його пріоритет збільшився - Завершився квант - Завершився потік
  • 11. Динамічне підвищення пріоритету - Після закінчення вводу/виводу - Після закінчення очікування - При пробудженні GUI потоків через операції з вікнами - Готовий до виконання потік довший час не має шансу виконатись Також при цьому може збільшуватись квант.
  • 12. Вибір CPU для потоку - Вибирається процесор, який простоює - Якщо немає вільного процесора – витіснення на ідеальному процесорі
  • 13. Scheduling granularity Windows планує потоки, не процеси Наслідок: Процес А має 1 потік Процес Б має 9 потоків Процес Б отримає 90% процесорного часу (за умови однакового пріоритету потоків)
  • 14. Threads in .NET - Створення потоку: - new Thread(Action) - Запуск: - Thread.Start() - Очікування доки потік завершиться: - Thread.Join()
  • 15. Thread pool - Пул потоків виконує завдання використовуючи вже створений набір потоків - Додання нового завдання: - ThreadPool.QueueUserWorkItem(delegate)
  • 16. UI message loop - Один GUI потік обробляє повідомлення з черги: - Clicks, repaints, mouse moves, key presses, … - Події обробляються послідовно - Отже, якщо одна подія займає довгий час, решта будуть чекати - Отже, довготривалі події ніколи не повинні виконуватись в UI потоці - IO, важкі обчислення
  • 17. Оновлення контролів - Щоб змінити контрол, код повинен виконуватись в UI потоці - Інші потоки можуть програмно додати повідомлення в чергу UI потоку
  • 18. Demo
  • 19. Task Parallel Library (TPL) Since .NET 4.0 http://msdn.microsoft.com/en-us/library/dd460717.aspx MSDN Parallel Computing Dev Center
  • 20. Task http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx Абстрагує значення, яке буде* доступне в майбутньому. Значення є результатом - Обчислення (CPU) - Вводу (IO) Task є .NET реалізацією future and promise
  • 21. Значення буде* доступне - Можливо вже є доступне - Таск завершився до звернення за результатом, - Також таск міг виконатись в цьому ж потоці - Можливо не буде, бо станеться помилка - Або обчислення буде відмінено
  • 22. Створення і запуск Task Task.Factory.StartNew(Action); Task<TResult> Task.Factory.StartNew(Func<TResult>); Або Task constructor + Task.Start() method
  • 23. Виконання Task – не потік. За замовчуванням таски виконуються потоками з thread pool Наслідок 1: таск може почати виконуватись не одразу Наслідок 2: довготривалий таск буде займати потік з thread pool (цей потік не зможе працювати над рештою задач)
  • 24. Виконання довготривалого Task`а Буде створено окремий потік Task.Factory.StartNew(() => { … }, TaskCreationOptions.LongRunning);
  • 25. Завершення, результат - Task.Wait() // with optional timeout - Task.Result Потік заблокується, поки таск не завершиться
  • 26. Очікування декількох тасків Task.WaitAll(Task[]) // with optional timeout Task.WaitAny(Task[])
  • 27. Demo
  • 28. Handle exceptions option 1 Task<int> t = Task.Factory.StartNew(() => { ... throw new InvalidOperationException("error"); return 42; }); Тут виникне Exception int result = t.Result; AggregateException AggregateException.InnerException(s) міститиме початковий exception
  • 29. Handle exceptions option 2 Check Task state: - Task.IsCompleted - Task.IsFaulted - Task.Exception AggregateException - Task.Status
  • 30. Unhandled exceptions Якщо не обробити exception, TPL його перевикине при фіналізації Task’a Викличеться подія TaskScheduler.UnobservedTaskException Помилка вважається обробленою при: - Виклику Task.Result, Task.Wait() etc. - Звертанні до Task.Exception
  • 31. Demo
  • 32. Continuations Задача: після завершення таска виконати наступний. В наступному таску – доступний результат попереднього. Task<U> Task<T>.ContinueWith(antecedent => {...}); TaskFactory.ContinueWhenAll(Task[], tasks => {...});
  • 33. Continuations, способи - One-to-one - Many-to-one - One-to-many - Chain
  • 34. Demo
  • 35. Деякі приклади використання Tasks - Досягнення паралелізму - Виконання CPU-bound задач паралельно - Виконання IO-bound задач паралельно - Responsive UI Demo
  • 37. Data race int count = 0; for (int i = 0; i < data.Length; i++) { var task = Task.Factory.StartNew(() => { count++; Not atomic! }); tasks.Add(task); } Task.WaitAll(tasks.ToArray());
  • 38. Preemption Thread A reg = read count (1) Thread B reg = read count (1) inc reg (2) write reg to count (2) inc reg (2) write reg to count (2)
  • 39. Рішення 1 – interlocked int count = 0; for (int i = 0; i < data.Length; i++) { var task = Task.Factory.StartNew(() => { Atomic, Interlocked.Increment(ref count); Flushes caches }); tasks.Add(task); } Task.WaitAll(tasks.ToArray());
  • 40. Рішення 2 – locking (critical section) int count = 0; object syncRoot = new object(); for (int i = 0; i < data.Length; i++) { var task = Task.Factory.StartNew(() => { lock (syncRoot) Other threads { cannot acquire held lock count++; } }); tasks.Add(task); } Task.WaitAll(tasks.ToArray());
  • 42. Parallel.For() and ForEach() Заблокується, доки всі ітерації не виконаються Parallel.For(0, data.Length, i => { Є можливість зробити Break, передавши state ProcessItem(data, i); }); Parallel.ForEach(data, value => { ProcessItem(value); });
  • 43. Parallel LINQ (PLINQ) var result = from value in data.AsParallel() where value % 2 == 0 let cube = Math.Pow(value, 3) let processed = ProcessItem(value) select new { Cube = cube, Result = processed }; parallelResult.ToArray(); Виконання почнеться тут
  • 44. Варто також дізнатись про: - Task.Status, Task lifecycle http://blogs.msdn.com/b/pfxteam/archive/2009/08/30/9889070.aspx - Continuations http://msdn.microsoft.com/en-us/library/dd235663 - TaskSchedulers http://msdn.microsoft.com/en-us/library/dd997402 - Cancellation http://msdn.microsoft.com/en-us/library/dd997364.aspx - Synchronization primitives http://msdn.microsoft.com/en- us/library/ms228964.aspx - Concurrent collections http://msdn.microsoft.com/en- us/library/system.collections.concurrent.aspx - Безплатна книжка, ще одна