Microsoft .NET Лекция 6: Параллелизм Меженин Михаил, кафедра "Системное программирование", ВМИ, ЮУрГУ, 2014 Microsoft .NET Параллелизм в .NET • Потоки / Threads • Задачи / Tasks 2 Microsoft .NET Threads using System; using System.Threading; static void main() { Thread t = new Thread (DoSomething); t.Start(); for (int i = 0; i < 1000; i++) Console.Write ("x"); } static void DoSomething() { for (int i = 0; i < 1000; i++) Console.Write ("y"); } 3 Microsoft .NET Threads using System; using System.Threading; static void main() { Thread t = new Thread (DoSomething); t.Start(); t.Join(); // синхронизация for (int i = 0; i < 1000; i++) Console.Write ("x"); } static void DoSomething() { for (int i = 0; i < 1000; i++) Console.Write ("y"); Thread.Sleep (500); // пауза } 4 Microsoft .NET Параллелизм в .NET Никакой безопасности! • Локальные переменные у каждого потока свои • Статические поля общие для всех потоков • Экземпляры классов и их поля общие для всех потоков 5 Microsoft .NET Threads class ThreadTest { bool done; static void main() { ThreadTest tt = new ThreadTest(); new Thread (t.DoSomething).Start(); tt.DoSomething(); } void DoSomething() { if (!done) { Console.Write ("Done"); done = false; } } } 6 Threads Microsoft .NET class ThreadTest { bool done; object myLock = new object(); ... void DoSomething() { lock (myLock) { if (!done) { Console.Write ("Done"); done = false; } } } } 7 Microsoft .NET Threads using System; using System.Threading; static void main() { Thread t = new Thread (() => DoSomething("X")); t.Start(); } // передача параметра // с помощью лямбда-выражения static void DoSomething(string s) { for (int i = 0; i < 1000; i++) Console.Write (s); } 8 Microsoft .NET Threads using System; using System.Threading; static void main() { Thread t = new Thread (() => DoSomething("X")); t.IsBackground = true; t.Start(); } // фоновый поток static void DoSomething(string s) { for (int i = 0; i < 1000; i++) Console.Write (s); } 9 Microsoft .NET Thread Pool Проблема: создание и удаление потоков – ресурсоемкая операция. Решение: повторное использование потоков, хранящихся в пуле void MyId() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); } for (int i = 0; i < 100; ++i) new Thread(MyId).Start(); Thread.Sleep(5000); for (int i = 0; i < 100; ++i) ThreadPool.QueueUserWorkItem(d => MyId()); 10 Microsoft .NET Tasks Недостатки потоков: • не возвращают результат • нельзя проверить статус выполнения Решение: новый API! using System.Threading.Tasks; var t1 = new Task(() => DoSomething("x")).Start(); t1.Wait(); // запуск // ждем завершения Task.Run(() => DoSomething("z")); // запуск Task<int> t = Task.Run(() => { return 3; }); var result = t.Result; // задача с возвращаемым значением // блокирующая проверка результата 11 Microsoft .NET Конвейер задач // длительная задача Task<int> primeNumberTask = Task.Run (() => Enumerable.Range (2, 3000000).Count (n => Enumerable.Range (2, (int)Math.Sqrt(n)-1).All (i => n % i > 0))); // запуск делегата после завершения задачи var awaiter = primeNumberTask.GetAwaiter(); awaiter.OnCompleted (() => { int result = awaiter.GetResult(); Console.WriteLine (result); // Writes result }); // запуск второй задачи после завершения первой primeNumberTask.ContinueWith (antecedent => { int result = antecedent.Result; Console.WriteLine (result); // Writes 123 }); 12 Microsoft .NET Асинхронные операции Синхронные методы: • блокируют поток • возвращают управление после выполнения • Console.WriteLine, Thread.Sleep Асинхронные методы: • • • • не блокируют поток возвращают управление сразу после вызова выполняются параллельно Thread.Start, Task.Run, ____Async await + async – простой способ последовательного написания кода, работающего асинхронно 13 Microsoft .NET Асинхронные операции var result = await expression; statement(s); var awaiter = expression.GetAwaiter(); awaiter.OnCompleted (() => { var result= awaiter.GetResult(); statement(s); ); 14 Microsoft .NET Асинхронные операции HttpClient client = new HttpClient(); Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); // асинхронный метод // выполнение занимает ~5c // вызов GetStringAsync не блокирует выполнение - Console.ReadKey выполнится Console.WriteLine("Loading..."); // выполнение будет остановлено, пока не выполнится GetStringAsync() Console.WriteLine(getStringTask.Result); Console.WriteLine("Done"); 15 Microsoft .NET Асинхронные операции // Вызываем асинхронный метод извне var v = AccessTheWebAsync(); … // Объявляем асинхронный метод async Task<int> AccessTheWebAsync() { HttpClient client = new HttpClient(); // Вызываем асинхронный метод GetStringAsync Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); // Выполняем независимую от GetStringAsync работу DoIndependentWork(); // Ждем выполнения задачи getStringTask и помещаем ее результат в urlContent // Пока задача не выполнена, возвращаем управление в код, вызвавший AccessTheWebAsync() string urlContents = await getStringTask; return urlContents.Length; } 16 Microsoft .NET Асинхронные операции 17 Microsoft .NET System.Threading.Tasks.Parallel Простой параллелизм для циклов: Parallel.For(0, 1000, i => DoWork(i)); Parallel.ForEach(collection, item => DoWork(item)); Parallel.Invoke( () => Method1(), () => Method2(), () => Method3()); 18 Microsoft .NET PLINQ Простой параллелизм для LINQ: "abcdef".AsParallel().Select (c => char.ToUpper(c)).ToArray(); 19