Главная

Популярная публикация

Научная публикация

Случайная публикация

Обратная связь

ТОР 5 статей:

Методические подходы к анализу финансового состояния предприятия

Проблема периодизации русской литературы ХХ века. Краткая характеристика второй половины ХХ века

Ценовые и неценовые факторы

Характеристика шлифовальных кругов и ее маркировка

Служебные части речи. Предлог. Союз. Частицы

КАТЕГОРИИ:






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




 

Теперь, когда мы дождались завершения работы нашего потока, пришло время получить результат его работы. Для этого служит метод EndInvoke делегата, использовавшегося для вызова BeginInvoke. Метод EndInvoke возвращает тот же объект, что вернул и выполнявшийся в отдельном потоке метод. Учтите, что эти методы должны вызываться у одного и того же экземпляра делегата. Методу EndInvoke передается объект IAsyncResult, возвращенный методом BeginInvoke:

Console.WriteLine("Divider of {0} is {1}.", 1000000021,dlgt.EndInvoke(res));

Учтите, что вызов EndInvoke до завершения работы потока приведет к блокировке вызвавшего его потока до тех пор, пока работа метода, выполняющегося в отдельном потоке не будет завершена. Поэтому обычно EndInvoke вызывают только дождавшись его завершения.

Если вы использовали IsCompleted или AsyncWaitHandle для ожидания окончания работы потока, то все в порядке. У вас есть и делегат и IAsyncResult. Но при использовании делегата обратного вызова есть проблема. Ему передается IAsyncResult в качестве параметра, но делегата, инициировавшего поток у него нет. Для того, чтобы получить его, используется обходной маневр:

AsyncResult aRes = res as AsyncResult;

GetDividerDelegate dlgt = aRes.AsyncDelegate as GetDividerDelegate;

 

Теперь можно спокойно вызывать EndInvoke. Если же такой искусственный подход вам не нравится, есть еще один способ. Методу BeginInvoke в последнем параметре вы можете передать любой объект. В последствии этот объект будет доступен через свойство AsyncState объекта IAsyncResult. Вы можете передать в качестве этого параметра сам делегат:

dlgt.BeginInvoke(1000000021, new AsyncCallback(GetDividerFinished), dlgt);

а затем в делегате обратного вызова извлечь его:

GetDividerDelegate dlgt = res.AsyncState as GetDividerDelegate;

Теперь вы так же можете вызывать EndInvoke.

Таким образом создание потоков с помощью делегатов имеет следующие достоинства и недостатки:

1. Легко создать поток на основе любого метода.

2. Легко дождаться завершения работы потока как синхронно (с блокированием ожидающего потока), так и асинхронно (без блокирования).

3. Можно получить результат работы метода, вызываемого в отдельном потоке.

4. Нельзя стандартными способами прервать работу потока или приостановить его, чтобы освободить вычислительные ресурсы. Вам придется писать для этого собственный код.

 

Класс Thread

 

Вторым способом создания отдельного потока является использование класса Thread. Необходимо создать объект класса Thread (программа ThreadingThread):

Thread trd = new Thread(new ThreadStart(CheckPrimeNumber));

Конструктору этого объекта передается экземпляр делегата ThreadStart. Он описывает метод без параметров, не возвращающий значения. Для запуска потока на выполнение нужно вызвать метод Start объекта Thread:

trd.Start();

Отсутствие возможности передать методу, который будет выполняться в отдельном потоке, каких либо параметров, несомненно является ограничением. Поэтому существует несколько иной синтаксис создания потока, который позволяет передать вашему методу параметры. При этом в конструктор класса Thread передается не делегат ThreadStart, а делегат ParameterizedThreadStart. Он описывает метод с одним параметром типа object:

Thread trd = new Thread(new ParameterizedThreadStart(CheckParameterPrimeNumber));

Теперь методу Start передается параметр, который будет передан вашему методу при выполнении его в отдельном потоке:

trd.Start(1000000021);

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

private static void CheckParameterPrimeNumber(object state)

{

long checkNumber = Convert.ToInt64(state);

Таким образом вы можете передавать в ваш метод несколько параметров, упаковав их в отдельный объект.

Недостатком такого подхода является отсутствие строгой типизации. Ничто не помешает мне вызвать метод Start с параметром типа string. Компилятор пропустит это, не выдав сообщения об ошибке. Тем не менее во время исполнения ошибка произойдет, поскольку string не может быть приведен к long. Стандартным методом решения этой проблемы является использование класса-обертки. Вы выносите метод, который вы хотите запустить в отдельном потоке, и параметры, необходимые для его работы, в отдельный класс:

class PrimeChecker

{

long checkNumber;

 

public PrimeChecker(long checkNumber)

{ this.checkNumber = checkNumber; }

 

public void Check()

{

for (long d = 2; d < checkNumber / 2; d++)

{

if (checkNumber % d == 0)

{

Console.WriteLine("{0} is not prime. Divider is {1}.", checkNumber, d);

return;

}

}

 

Console.WriteLine("{0} is prime.", checkNumber);

}

}

 

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

PrimeChecker pc = new PrimeChecker(1000000021);

Thread trd = new Thread(new ThreadStart(pc.Check));

trd.Start();

 

Этот прием позволяет сохранить строгую типизацию.

 






Не нашли, что искали? Воспользуйтесь поиском:

vikidalka.ru - 2015-2024 год. Все права принадлежат их авторам! Нарушение авторских прав | Нарушение персональных данных