Главная страница

презентация параллельное программирование. Параллельное программирование. Параллельное программирование. Преподаватель Кривошеина А. В


Скачать 115.53 Kb.
НазваниеПараллельное программирование. Преподаватель Кривошеина А. В
Анкорпрезентация параллельное программирование
Дата28.04.2022
Размер115.53 Kb.
Формат файлаpptx
Имя файлаПараллельное программирование.pptx
ТипДокументы
#501955

Параллельное программирование.

Преподаватель Кривошеина А.В.

ЧУО «Колледж бизнеса и права»

  • В эпоху многоядерных машин, которые позволяют параллельно выполнять сразу несколько процессов, стандартных средств работы с потоками в .NET уже оказалось недостаточно. Поэтому во фреймворк .NET была добавлена библиотека параллельных задач TPL (Task Parallel Library), основной функционал которой располагается в пространстве имен System.Threading.Tasks.
  • Данная библиотека позволяет распараллелить задачи и выполнять их сразу на нескольких процессорах, если на целевом компьютере имеется несколько ядер. Кроме того, упрощается сама работа по созданию новых потоков.

Задачи и класс Task

  • В основе библиотеки TPL лежит концепция задач, каждая из которых описывает отдельную продолжительную операцию. В библиотеке классов .NET задача представлена специальным классом - классом Task, который находится в пространстве имен System.Threading.Tasks.
  • Данный класс описывает отдельную задачу, которая запускается асинхронно в одном из потоков из пула потоков. Хотя ее также можно запускать синхронно в текущем потоке.

Первый способ :

  • создание объекта Task и вызов у него метода Start:
  • Task task = new Task(() => Console.WriteLine ("Hello Task!"));

    task.Start();

  • В качестве параметра объект Task принимает делегат Action, то есть мы можем передать любое действие, которое соответствует данному делегату, например, лямбда-выражение, как в данном случае, или ссылку на какой-либо метод. То есть в данном случае при выполнении задачи на консоль будет выводиться строка "Hello Task!".
  • А метод Start() собственно запускает задачу.

Второй способ:

  • использование статического метода Task.Factory.StartNew().
  • Этот метод также в качестве параметра принимает делегат Action, который указывает, какое действие будет выполняться. При этом этот метод сразу же запускает задачу:
  • Task task = Task.Factory.StartNew(() => Console.WriteLine("Hello Task!"));

  • В качестве результата метод возвращает запущенную задачу.

Третий способ:

  • использование статического метода Task.Run():
  • Task task = Task.Run(() => Console.WriteLine("Hello Task!"));

  • Метод Task.Run() также в качестве параметра может принимать делегат Action - выполняемое действие и возвращает объект Task.

using System;

using System.Threading.Tasks;

namespace HelloApp

{

class Program

{

static void Main(string[] args)

{

Task task1 = new Task(() => Console.WriteLine("Task1 is executed"));

task1.Start();

Task task2 = Task.Factory.StartNew(() => Console.WriteLine("Task2 is

executed"));

Task task3 = Task.Run(() => Console.WriteLine("Task3 is executed"));

Console.ReadLine();

}

}

}

Ожидание задачи


using System;

using System.Threading;

namespace TaskApp

{

class Program

{

static void Main(string[] args)

{

Task task = new Task(Display);

task.Start();

Console.WriteLine("Завершение метода Main");

Console.ReadLine();

}

static void Display()

{

Console.WriteLine("Начало работы метода Display");

Console.WriteLine("Завершение работы метода Display");

}

}

}

консольный вывод может выглядеть следующим образом:

Завершение метода Main

Начало работы метода Display

Завершение работы метода Display

Чтобы указать, что метод Main должен подождать до конца выполнения задачи, нам надо использовать метод Wait:


static void Main(string[] args)

{

Task task = new Task(Display);

task.Start();

task.Wait();

Console.WriteLine("Завершение метода Main");

Console.ReadLine();

}

Свойства класса Task:

  • AsyncState: возвращает объект состояния задачи
  • CurrentId: возвращает идентификатор текущей задачи
  • Exception: возвращает объект исключения, возникшего при выполнении задачи
  • Status: возвращает статус задачи

Работа с классом Task Вложенные задачи


static void Main(string[] args)

{

var outer = Task.Factory.StartNew(() => // внешняя задача

{

Console.WriteLine("Outer task starting...");

var inner = Task.Factory.StartNew(() => // вложенная задача

{

Console.WriteLine("Inner task starting...");

Thread.Sleep(2000);

Console.WriteLine("Inner task finished.");

});

});

outer.Wait(); // ожидаем выполнения внешней задачи

Console.WriteLine("End of Main");

Console.ReadLine();

}

Если необходимо, чтобы вложенная задача выполнялась вместе с внешней, необходимо использовать значение TaskCreationOptions.AttachedToParent:


static void Main(string[] args)

{

var outer = Task.Factory.StartNew(() => // внешняя задача

{

Console.WriteLine("Outer task starting...");

var inner = Task.Factory.StartNew(() => // вложенная задача

{

Console.WriteLine("Inner task starting...");

Thread.Sleep(2000);

Console.WriteLine("Inner task finished.");

}, TaskCreationOptions.AttachedToParent);

});

outer.Wait(); // ожидаем выполнения внешней задачи

Console.WriteLine("End of Main");

Console.ReadLine();

}

Task[] tasks1 = new Task[3]

{

new Task(() => Console.WriteLine("First Task")),

new Task(() => Console.WriteLine("Second Task")),

new Task(() => Console.WriteLine("Third Task"))

};

// запуск задач в массиве

foreach (var t in tasks1)

t.Start();

Также как и с потоками, мы можем создать и запустить массив задач. Можно определить все задачи в массиве непосредственно через объект Task:

Task[] tasks2 = new Task[3];

int j = 1;

for (int i = 0; i < tasks2.Length; i++)

tasks2[i] = Task.Factory.StartNew(() => Console.WriteLine($"Task {j++}"));

Либо также можно использовать методы Task.Factory.StartNew или Task.Run и сразу запускать все задачи:

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


static void Main(string[ ] args)

{

Task[ ] tasks1 = new Task[3]

{

new Task(() => Console.WriteLine("First Task")),

new Task(() => Console.WriteLine("Second Task")),

new Task(() => Console.WriteLine("Third Task"))

};

foreach (var t in tasks1)

t.Start();

Task[] tasks2 = new Task[3];

int j = 1;

for (int i = 0; i < tasks2.Length; i++)

tasks2[i] = Task.Factory.StartNew(() => Console.WriteLine($"Task {j++}"));

Console.WriteLine("Завершение метода Main"); Console.ReadLine();

}

Один из возможных консольных выводов данной программы:


Second Task

Task 1

Завершение метода Main

Third Task

Task 3

First Task

Task 2

Если необходимо выполнять некоторый код лишь после того, как все задачи из массива завершатся, то применяется метод Task.WaitAll(tasks):


static void Main(string[] args)

{

Task[] tasks1 = new Task[3]

{

new Task(() => Console.WriteLine("First Task")),

new Task(() => Console.WriteLine("Second Task")),

new Task(() => Console.WriteLine("Third Task"))

};

foreach (var t in tasks1)

t.Start();

Task.WaitAll(tasks1); // ожидаем завершения задач

Console.WriteLine("Завершение метода Main");

Console.ReadLine();

}

Возвращение результатов из задач

  • Задачи могут не только выполняться как процедуры, но и возвращать определенные результаты:

class Program

{

static void Main(string[] args)

{

Task task1 = new Task(()=>Factorial(5));

task1.Start();

Console.WriteLine($"Факториал числа 5 равен {task1.Result}");

Task task2 = new Task(() =>

{

return new Book { Title = "Война и мир", Author = "Л. Толстой" };

});

task2.Start();

Book b = task2.Result; // ожидаем получение результата

Console.WriteLine($"Название книги: {b.Title}, автор: {b.Author}");

Console.ReadLine();

}

static int Factorial(int x)

{

int result = 1;

for (int i = 1; i <= x; i++)

{

result *= i;

}

return result;

}

}

public class Book

{

public string Title { get; set; }

public string Author { get; set; }

}
  • Во-первых, чтобы задать возвращаемый из задачи тип объекта, мы должны типизировать Task. Например, Task - в данном случае задача будет возвращать объект int.
  • И, во-вторых, в качестве задачи должен выполняться метод, возвращающий данный тип объекта. Например, в первом случае у нас в качестве задачи выполняется функция Factorial, которая принимает числовой параметр и также на выходе возвращает число.
  • Возвращаемое число будет храниться в свойстве Result: task1.Result. Нам не надо его приводить к типу int, оно уже само по себе будет представлять число.
  • То же самое и со второй задачей task2. В этом случае в лямбда-выражении возвращается объект Book. И также мы его получаем с помощью task2.Result
  • При этом при обращении к свойству Result программа текущий поток останавливает выполнение и ждет, когда будет получен результат из выполняемой задачи.

Задачи продолжения

  • Задачи продолжения или continuation task позволяют определить задачи, которые выполняются после завершения других задач. Благодаря этому мы можем вызвать после выполнения одной задачи несколько других, определить условия их вызова, передать из предыдущей задачи в следующую некоторые данные.
  • Задачи продолжения похожи на методы обратного вызова, но фактически являются обычными задачами Task. Рассмотрим на примере:

using System;

using System.Threading;

using System.Threading.Tasks;

namespace TaskApp

{

class Program

{

static void Main(string[] args)

{

Task task1 = new Task(()=>{

Console.WriteLine($"Id задачи: {Task.CurrentId}");

});

// задача продолжения

Task task2 = task1.ContinueWith(Display);

task1.Start();

// ждем окончания второй задачи

task2.Wait();

Console.WriteLine("Выполняется работа метода Main");

Console.ReadLine();

}

static void Display(Task t)

{

Console.WriteLine($"Id задачи: {Task.CurrentId}");

Console.WriteLine($"Id предыдущей задачи: {t.Id}");

Thread.Sleep(3000);

}

}

}

И после завершения задачи task1 сразу будет вызываться задача task2.

Также мы можем передавать конкретный результат работы предыдущей задачи:


using System;

using System.Reflection;

using System.Runtime.InteropServices;

using System.Threading;

using System.Threading.Tasks;

namespace HelloApp

{

class Program

{

static void Main(string[] args)

{

Task task1 = new Task(()=>Sum(4,5));

// задача продолжения

Task task2 = task1.ContinueWith(sum => Display(sum.Result));

task1.Start();

// ждем окончания второй задачи

task2.Wait();

Console.WriteLine("End of Main");

Console.ReadLine();

}

static int Sum(int a, int b) => a + b;

static void Display(int sum)

{

Console.WriteLine($"Sum: {sum}");

}

}

}

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


static void Main(string[] args)

{

Task task1 = new Task(()=>{

Console.WriteLine($"Id задачи: {Task.CurrentId}");

});

// задача продолжения

Task task2 = task1.ContinueWith(Display);

Task task3 = task1.ContinueWith((Task t) =>

{

Console.WriteLine($"Id задачи: {Task.CurrentId}");

});

Task task4 = task2.ContinueWith((Task t) =>

{

Console.WriteLine($"Id задачи: {Task.CurrentId}");

});

task1.Start(); Console.ReadLine();

}

static void Display(Task t)

{

Console.WriteLine($"Id задачи: {Task.CurrentId}");

}


написать администратору сайта