Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Многопоточность.docx
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
113.32 Кб
Скачать

Получение результатов выполнения потока

Класс Task позволяет получить результаты выполнения потока. Для этого используется generic-версия этого класса Task<T> (класс Exercise9). Пусть у нас есть метод, возвращающий некоторое значение:

private static long GetDivider(object state)

И мы хотим исполнить его асинхронно и получить результат. Для этого создается экземпляр класса Task<long>:

Task<long> task = Task.Factory.StartNew<long>(GetDivider, 1000000021);

Затем необходимо любым способом дождаться завершения выполнения задачи и получить результат через свойство Result:

Console.WriteLine("Divider of {0} is {1}.", 1000000021, task.Result);

Прерывание потока

В .NET 4 появился новый мощный механизм отмены асинхронных или долгих синхронных операций. Он основан на использовании объекта, называемого токеном отмены (cancellation token). Давайте рассмотрим простейший пример его использования (класс Exercise10).

Получить экземпляр объекта токена отмены можно с помощью свойства Token класса CancellationTokenSource:

CancellationTokenSource ctSource = new CancellationTokenSource();

CancellationToken token = ctSource.Token;

Теперь созданный токен нужно передать в тот метод, который будет выполняться в отдельном потоке. Теперь нужно передать созданный токен в ваш метод, чтобы он мог узнать, когда его попросят прерваться. Так же нужно передать токен конструктору объекта Task. Зачем это нужно, я скажу позже:

var task = Task.Factory.StartNew<long>(() => GetDivider(1000000021, token), token);

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

Послать уведомление о том, что потоку нужно прерваться, очень просто. Для этого нужно вызвать метод Cancel объекта CancellationTokenSource:

ctSource.Cancel();

В этом случае все токены отмены, созданные с помощью свойства Token этого объекта, узнают о том, что требуется отменить выполнение потока. Однако то, какие действия должен при этом выполнять поток, является выбором программиста. Внутри вашего метода вы можете узнать, была ли запрошена отмена, с помощью свойства IsCancellationRequested объекта CancellationToken:

if (cToken.IsCancellationRequested)

{

// some cleaning code.

cToken.ThrowIfCancellationRequested();

}

Этот код является стандартным кодом прерывания потока. Вы проверяете, была ли запрошена отмена выполнения, затем выполняете некоторый код очистки, а потом вызываете метод ThrowIfCancellationRequested. Последний генерирует OperationCanceledException только в том случае, если была запрошена отмена выполнения.

Что же происходит, когда генерируется это исключение? Объект OperationCanceledException несет в себе информацию о том, какой именно токен отмены создал его. Если этот токен не совпадает с тем, что был передан конструктору Task, то данное исключение расценивается как обычное сообщение об ошибке, поток прерывается и свойство Status объекта Task устанавливается в Faulted. Если же токены совпадают, то значение свойства Status делается равным Canceled. При этом, если кто-либо ожидает завершения данного потока (например, с помощью метода Wait), то он получит исключение AggregateException. Это новый тип исключения, который содержит информацию о нескольких исключениях. Это необходимо для того, чтобы отлавливать исключения, произошедшие сразу в нескольких потоках. Все произошедшие исключения доступны через свойство InnerExceptions:

try

{

task.Wait();

}

catch (AggregateException aex)

{

foreach(var ex in aex.InnerExceptions )

{

Console.WriteLine(ex);

}

}

С помощью токенов отмены вы можете прерывать выполнение нескольких потоков и цепочек потоков, передавая им объект токена и используя описанные здесь приемы. Класс CancellationToken так же может быть полезен для отмены асинхронных операций (см. метод Register).

Подведем итоги. Класс Task имеет следующие достоинства и недостатки:

  1. Простой способ создания отдельных потоков.

  2. Существуют синхронные и асинхронные механизмы ожидания завершения потока.

  3. Есть возможность отменить выполнение потока.

  4. Существует легкая возможность составлять цепочки из потоков.

  5. Легко получить результат выполнения метода потока.

  6. Немного затруднен механизм передачи типизированных параметров в поток.

  7. Невозможно приостанавливать поток и запускать его снова.

PLINQ

.NET Framework 4.0 принес с собой еще одну возможность, связанную с многопоточностью. Она связана с обработкой коллекций (проект PLINQ). Бывают случаи, когда нам нужно обработать в отдельных потоках элементы какой-либо коллекции. Раньше нам пришлось бы создавать цикл, в котором вызывать новый поток для каждого из элементов. Теперь .NET Framework предлагает упрощенный синтаксис.