Помощь в написании студенческих работ
Антистрессовый сервис

Суммирование в Инструментарии.net

РефератПомощь в написанииУзнать стоимостьмоей работы

Основные классы для реализации многопоточных приложений определены в пространстве имен System.Threading. Для описания собственных потоков предназначен класс Thread. При создании потока ему необходимо указать делегата, реализующего процедуру потока. К сожалению, в .NET, во-первых, не предусмотрено передачи аргументов в эту процедуру, во-вторых, процедура должна быть статическим методом… Читать ещё >

Суммирование в Инструментарии.net (реферат, курсовая, диплом, контрольная)

Потоки в .NET реализованы на основе модели потоков операционной системы и не предусматривают средств управления волокнами. Если система не поддерживает многопоточные приложения, то PAL должен будет реализовать собственный планировщик потоков режима пользователя (и в этом случае окажется, что потоки .NET являются аналогами волокон, а не потоков ядра).

Основные классы для реализации многопоточных приложений определены в пространстве имен System.Threading. Для описания собственных потоков предназначен класс Thread. При создании потока ему необходимо указать делегата, реализующего процедуру потока. К сожалению, в .NET, во-первых, не предусмотрено передачи аргументов в эту процедуру, во-вторых, процедура должна быть статическим методом, и в-третьих, класс Thread является опечатанным. В результате передача каких-либо данных в процедуру потока вызывает определенные трудности и требует явного или косвенного использования статических полей, что не слишком удобно, зачастую нуждается в дополнительной синхронизации и плохо соответствует парадигме ООП.

Приведенный ниже пример демонстрирует работу с созданием нескольких потоков для параллельного перемножения двух квадратных матриц. Начальные значения всех элементов матриц равны 1, поэтому результирующая матрица должна быть заполнена числами, равными размерности перемножаемых матриц. В процессе умножения и суммирования элементов матриц синхронизация не выполняется, поэтому при достаточно большом размере матриц гарантированно будут возникать ошибки (необходимо синхронизировать выполнение некоторых действий в потоках, чтобы избежать возникновения ошибок, — об этом ниже, при рассмотрении средств синхронизации):

using System;

using System. Threading;

namespace TestNamespace {.

class TestApp {.

const int m_size = 600;

const int m_stripsize = 50;

const int m_stripmax = 12;

private static int m_stripused = 0;

private static double[,] m_A = new double[m_size, m_size],.

m_B = new double[m_size, m_size],.

m_C = new double[m_size, m_size];

public static void ThreadProc ().

{.

int i, j, k, from, to;

from = (m_stripused++) * m_stripsize;

to = from + m_stripsize;

if (to > m_size) to = m_size;

for (i = 0; i < m_size; i++) {.

for (j = 0; j < m_size; j++) {.

for (k = from; k < to; k++).

m_C[i, j] += m_A[i, k] * m_B[k, j];

}.

}.

}.

public static void Main ().

{.

Thread[] T = new Thread[ m_stripmax ];

int i, j, errs;

for (i = 0; i < m_size; i++) {.

for (j = 0; j < m_size; j++) {.

m_A[i, j] = m_B[i, j] = 1.0;

m_C[i, j] = 0.0;

}.

}.

for (i = 0; i < m_stripmax; i++) {.

T[i] = new Thread (new ThreadStart (ThreadProc));

T[i]. Start ();

}.

// дожидаемся завершения всех потоков.

for (i = 0; i < m_stripmax; i++) T[i]. Join ();

// проверяем результат.

errs = 0;

for (i = 0; i < m_size; i++).

for (j = 0; j < m_size; j++).

if (m_C[i, j] ≠ m_size) errs++;

Console.WriteLine («Error count = {0}», errs);

}.

}.

}.

Поток в .NET может находиться в одном из следующих состояний: незапущенном, исполнения, ожидания, приостановленном, завершенном и прерванном. Возможные переходы между этими состояниями изображены на рис. 1.

 Состояния потока.

Рис. 1. Состояния потока Сразу после создания и до начала выполнения потока он находится в незапущенном состоянии (Unstarted). Текущее состояние можно определить с помощью свойства Thread.ThreadState. После запуска поток можно перевести в состояние исполнения (Running) вызовом метода Thread.Start. Работающий поток может быть переведен в состояние ожидания (WaitSleepJoin) явным или неявным вызовом соответствующих методов (Thread.Sleep, Thread. Join и др.) или приостановлен (Suspended) с помощью метода Thread.Suspend(). Исполнение приостановленного потока можно возобновить вызовом метода Thread.Resume. Также можно досрочно вывести поток из состояния ожидания вызовом метода Thread.Interrupt.

Завершение функции потока нормальным образом переводит поток в состояние «завершен» (Stopped), а досрочное прекращение работы вызовом метода Thread. Abort переведет его в состояние «прерван» (Aborted). Кроме того, .NET поддерживает несколько переходных состояний (AbortRequested, StopRequested и SuspendRequested). Состояния потока в общем случае могут комбинироваться, например, вполне корректно сочетание состояния ожидания (WaitSleepJoin) и какого-либо переходного, скажем, AbortRequested.

Для выполнения задержек в ходе выполнения потока предназначены два метода — Sleep, переводящий поток в состояние ожидания на заданное время, и SpinWait, который выполняет некоторую задержку путем многократных повторов внутреннего цикла. Этот метод дает высокую загрузку процессора, однако позволяет реализовать очень короткие паузы. К сожалению, продолжительность пауз зависит от производительности и загруженности процессора.

Для получения и задания приоритета потока используется свойство Thread.Priority. Приоритеты потока в .NET базируются на подмножестве относительных приоритетов Win32 API так, что при переносе на другие платформы существует возможность предоставить их корректные аналоги. В .NET используются приоритеты Highest, AboveNormal, Normal, BelowNormal и Lowest.

Когда .NET приложение начинает исполняться в среде Windows, CLR создает внутренний пул потоков, используемый средой для реализации асинхронных операций ввода-вывода, вызова асинхронных процедур, обработки таймеров и других целей. Потоки могут добавляться в пул по мере надобности. Этот пул реализуется на основе пула потоков, управляемого операционной системой (построенного на основе порта завершения ввода-вывода). Для взаимодействия с пулом потоков предусмотрен класс ThreadPool, и единственный объект, принадлежащий этому классу, создается CLR при запуске приложения. Все домены приложений в рамках одного процесса используют общий пул потоков.

Разработчики могут использовать несколько статических методов класса ThreadPool. Так, например, существует возможность связать внутренний порт завершения ввода-вывода с файловым объектом, созданным неуправляемым кодом, для обработки событий, связанных с завершением ввода-вывода этим объектом (см. методы ThreadPool. BindHandle и описание порта завершения ввода-вывода ранее). Можно управлять числом потоков в пуле (методы GetAvailableThreads, GetMaxThreads, GetMinThreads и SetMinThreads), можно ставить в очередь асинхронных вызовов собственные процедуры (методQueueUserWorkItem) и назначать процедуры, которые будут вызываться при освобождении какого-либо объекта (методRegisterWaitForSingleObject). Эти два метода имеют «безопасные» и «небезопасные» (Unsafe…) версии; последние отличаются тем, что в стеке вызовов асинхронных методов не будут присутствовать данные о реальном контексте безопасностипотока, поставившего в очередь этот вызов, — в подобном случае будет использоваться контекст безопасности самого пула потоков.

Существует такой класс как ThreadPool, который является менеджером потоков, и вы с лёгкостью можете делегировать задачи без надобности постоянно создавать новые потоки, что в очередной раз хорошо для производительности. Здесь нельзя не согласиться, если речь идёт о простеньких приложениях, но если требуется серьёзная оптимизация — забудьте! Вам нужен либо свой менеджер асинхронных задач, либо готовые решения.

Представьте что у вас всего 2 ядра у процессора, и размер пула потоков тоже 2. Вы хотите выполнить 3 асинхронные задачи:

первая считает общее количество бит в большом массиве данных в памяти.

вторая читает данные из сетевого протокола, и пишет на диск третья шифрует небольшое значение с помощью AES-256.

Вы добавляете задачи поочерёдно в пул, и надеетесь, что это самый эффективный способ решения — как только одна из первых двух задач закончит выполнение, начнёт выполняться третья. Это не так. Самым эффективным решением будет выполнить все три задачи параллельно, и связано это с использованием ресурсов. Первая задача больше всего использует оперативную память, и немного процессор, вторая задача больше всего использует жёсткий диск и сетевое оборудование, а третья больше всего нагружает процессор. Поэтому, если выполнять только первые две задачи параллельно, то ресурс «процессор» будет простаивать, в то время как его можно задействовать на полную мощность.

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

Показать весь текст
Заполнить форму текущей работой