Межпроцессорное взаимодействие и многопоточность в .NET (реферат), страница 3
Описание файла
Документ из архива "Межпроцессорное взаимодействие и многопоточность в .NET (реферат)", который расположен в категории "". Всё это находится в предмете "параллельные системы и параллельные вычисления" из 9 семестр (1 семестр магистратуры), которые можно найти в файловом архиве НИУ «МЭИ» . Не смотря на прямую связь этого архива с НИУ «МЭИ» , его также можно найти и в других разделах. Архив можно найти в разделе "остальное", в предмете "параллельные системы и параллельные вычисления" в общих файлах.
Онлайн просмотр документа "Межпроцессорное взаимодействие и многопоточность в .NET (реферат)"
Текст 3 страницы из документа "Межпроцессорное взаимодействие и многопоточность в .NET (реферат)"
using System.Diagnostics;
using System.ComponentModel;
namespace MyProcessSample
{
///
/// Пример
///
class MyProcess
{
// Ошибки (родные виндовские)
const int ERROR_FILE_NOT_FOUND = 2;
const int ERROR_ACCESS_DENIED = 5;
///
/// Печать файла с расширением .doc
///
void PrintDoc()
{
Process myProcess = new Process();
try
{
// Путь к папочке “Мои документы”
string myDocumentsPath =
Environment.GetFolderPath(Environment.SpecialFolder.Personal);
// Настраиваем процесс на печать
myProcess.StartInfo.FileName = myDocumentsPath + "\\MyFile.doc";
myProcess.StartInfo.Verb = "Print";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();
}
// Обработка исключений
catch (Win32Exception e)
{
if (e.NativeErrorCode == ERROR_FILE_NOT_FOUND)
{
Console.WriteLine(e.Message + ". Check the path.");
}
else if (e.NativeErrorCode == ERROR_ACCESS_DENIED)
{
Console.WriteLine(e.Message +
". You do not have permission to print this file.");
}
}
}
// Главный процесс
public static void Main()
{
// Печать производим в отдельном процессе
MyProcess myProcess = new MyProcess();
myProcess.PrintDoc();
Console.Read();
}
}
}
-
Пример использования свойства ProcessorAffinity
int MyID;
int p = 2;//Номер логического процессора
private void ThreadToProcessor()
{
//Конфигурируем и запускаем поток
th = new System.Threading.Thread(new System.Threading.ThreadStart(ThCallBack));
th.Name = "Test_Thread";
th.Start();
ProcessThread ProcThr = null;
bool IsFound = false;
IntPtr proc;
Process currentProcess = Process.GetCurrentProcess();
//Получаем коллекцию потоков текущего процесса
ProcessThreadCollection theThreads = currentProcess.Threads;
for (int i=0;i { ProcThr = theThreads[i]; //Ищем нужный поток для запска его на другом ядре машины if (ProcThr.Id == MyID) { IsFound = true; break; } } if (IsFound) { //Поток th работает на ядре p = 2 proc = new IntPtr(1<<(p-1)); ProcThr.ProcessorAffinity = proc; } } private void ThCallBack() { MyID = AppDomain.GetCurrentThreadId(); while(true) { } } Описание: Абстрактный класс, который инкапсулирует в себе средства для синхронизации доступа к разделяемым данным. Этими средствами являются классы Mutex, AutoResetEvent, ManualResetEvent (подробная информация о данных классах размещена в соответствующих разделах). Этот класс используется в качестве базового для объектов синхронизации. Классы, производные от WaitHandle, определяют механизм сигналов для того, чтобы показать на изъятие или освобождение эксклюзивного доступа к общему ресурсу, но эти классы используют унаследованные методы от WaitHandle для блокирования во время ожидания доступа к общим ресурсам. Следует использовать статические методы этого класса для блокирования потока до получения сигнала одним или более объектами синхронизации. Иерархия: System.Object Определение: [C#] public abstract class WaitHandle : MarshalByRefObject, IDisposable Потокобезопасность: Этот тип можно использовать в многопоточных операциях. WaitTimeout Показывает, что время ожидания операции WaitAny истекло до того, как какой-либо из ожидающих дескрипторов получил сигнал. Данное поле является константой. Handle Получает или задает собственный дескриптор операционной системы. WaitAll Ожидает получения сигнала всеми элементами заданного массива. WaitAny Ожидает получения сигнала каким – либо элементом заданного массива. WaitOne Блокирует текущий поток до получения сигнала текущим объектом WaitHandle. Определение: public const int WaitTimeout public virtual IntPtr Handle { get; set; } public static bool WaitAll ( WaitHandle[] waitHandles) public static int WaitAny (WaitHandle[] waitHandles) public virtual bool WaitOne () Примеры Соответствующие примеры о данном классе можно найти в разделах Mutex, AutoResetEvent, ManualResetEvent Описание: Примитив синхронизации, который также может использоваться в межпроцессорной синхронизации. Когда двум или более потокам нужно произвести доступ к разделяемому ресурсу одновременно, системе необходим механизм синхронизации для того, чтобы гарантировать использование ресурса только одним потоком. Класс Mutex — это примитив синхронизации, который предоставляет эксклюзивный доступ к разделяемому ресурсу только для одного потока. Если поток получает Mutex, второй поток, желающий получить этот Mutex, приостанавливается до тех пор, пока первый поток не освободит данный Mutex. Можно использовать метод WaitHandle.WaitOne для запроса на владение объектом Mutex. Поток, владеющий объектом Mutex, может запрашивать его в повторяющихся вызовах Wait без блокировки выполнения. Однако поток должен вызвать метод ReleaseMutex соответственное количество раз для того, чтобы прекратить владеть объектом Mutex. Если поток завершается нормально во время владения объектом Mutex, состояние объекта Mutex задается сигнальным, и следующий ожидающий поток становится владельцем семафора. Если нет потоков, владеющих объектом Mutex, его состояние является сигнальным. Иерархия: System.Object Определение: [C#] public sealed class Mutex : WaitHandle Потокобезопасность: Этот тип можно использовать в многопоточных операциях. Handle (унаследовано от WaitHandle) Получает или задает собственный дескриптор операционной системы. Close (унаследовано от WaitHandle) При переопределении в производном классе освобождает все ресурсы, занимаемые текущим объектом WaitHandle. ReleaseMutex Освобождает объект Mutex один раз. WaitOne (унаследовано от WaitHandle) Блокирует текущий поток до получения сигнала текущим объектом WaitHandle (сигнал о том что разделяемый ресурс свободен) Определение: public virtual IntPtr Handle { get; set; } public virtual void Close () public void ReleaseMutex () public virtual bool WaitOne () Пример демонстрирует, как нужно использовать Mutex для синхронизации доступа к разделяемым данным. Класс – Mutex может использоваться совместно с классом WaitHandle (в отличие от класса Monitor), так как является дочерним от WaitHandle. using System; using System.Threading; class Test { //Создаем объект Mutex private static Mutex mut = new Mutex(); private const int numIterations = 1; private const int numThreads = 3; static void Main() { //Создаем потоки, которые будут конкурировать за использование //разделяемых ресурсов for (int i = 0; i < numThreads; i++) { Thread myThread = new Thread(new ThreadStart(MyThreadProc)); myThread.Name = String.Format("Thread{0}", i + 1); myThread.Start(); } //Главный поток завершает свою работу, но приложение продолжает //выполняться, пока все приоритетные потоки не завершат свою работу } private static void MyThreadProc() { for (int i = 0; i < numIterations; i++) { UseResource(); } } //Метод UseResource() представляет собой ресурс доступ к которому //должен быть синхронизирован между потоками. Только один поток //поток должен иметь доступ к ресурсу private static void UseResource() { //Точка синхронизации(ждем когда можно безопасно //использовать ресурс) mut.WaitOne(); Console.WriteLine("{0} has entered the protected area", Thread.CurrentThread.Name); // Выполнение некоторой работы Thread.Sleep(500); Console.WriteLine("{0} is leaving the protected area\r\n", Thread.CurrentThread.Name); // Убираем ресурсы mut.ReleaseMutex(); } } Выходная информация: Thread1 has entered the protected area Thread1 is leaving the protected area Thread3 has entered the protected area Thread3 is leaving the protected area Thread2 has entered the protected area Thread2 is leaving the protected area Описание: Дочерний от WaitHandle класс предназначен для оповещения ожидающего потока о том, что произошло событие (доступен или недоступен ресурс) AutoResetEvent позволяет потокам взаимодействовать друг с другом путем передачи сигналов. Обычно это взаимодействие касается ресурса, к которому потокам необходим эксклюзивный доступ. Поток ожидает сигнала, вызывая метод WaitOne при возникновении AutoResetEvent. Если AutoResetEvent находится в несигнальном состоянии, поток будет заблокирован, ожидая сигнала потока, в настоящий момент контролирующего ресурс, о том, что ресурс доступен (для этого вызывается метод Set). Вызов Set сигнализирует AutoResetEvent, что надо освободить ожидающий поток. AutoResetEvent остается в сигнальном состоянии до тех пор, пока один ожидающий поток не будет освобожден, а затем возвращается в несигнальное состояние. Если нет ожидающих потоков, состояние остается сигнальным бесконечно. Можно контролировать начальное состояние AutoResetEvent, передав конструктору логическое значение, значение true, если начальное состояние сигнальное, и false в противном случае. Иерархия: System.Object Определение: [C#] public sealed class AutoResetEvent : WaitHandle Потокобезопасность: Этот тип можно использовать в многопоточных операциях. Close При переопределении в производном классе освобождает все ресурсы, занимаемые текущим объектом WaitHandle. Reset Задает несигнальное состояние указанного события. Set Задает сигнальное состояние указанного события. WaitOne Блокирует текущий поток до получения сигнала текущим объектом WaitHandle. Определение: public virtual void Close(); public bool Reset(); public bool Set(); public virtual bool WaitOne(); Пример использования класса AutoResetEvent using System; using System.Threading; class WaitOne { static AutoResetEvent autoEvent = new AutoResetEvent(false); static void Main() { Console.WriteLine("Main starting."); //Ставим метод в очередь на выполнение ThreadPool.QueueUserWorkItem( new WaitCallback(WorkMethod), autoEvent); //Ждем обработки методом сигнала о завершении WorkMethod() autoEvent.WaitOne(); Console.WriteLine("Work method signaled.\nMain ending."); } static void WorkMethod(object stateInfo) { Console.WriteLine("Work starting."); // Симуляция работы Thread.Sleep(new Random().Next(1000, 8000)); //Сигнал о том что работа закончена Console.WriteLine("Work ending."); ((AutoResetEvent)stateInfo).Set(); } } Выходная информация: Main starting. Work starting. Work ending. Work method signaled. Main ending. Описание: Дочерний от WaitHandle класс ManualResetEvent предназначен для оповещения ожидающего потока или нескольких ожидающих потоков о том, что произошло событие (доступен или недоступен ресурс) ManualResetEvent позволяет потокам взаимодействовать друг с другом путем передачи сигналов. Обычно это взаимодействие касается задачи, которую один поток должен завершить до того, как другой продолжит работу. Когда поток начинает работу, которая должна быть завершена до продолжения работы других потоков, он вызывает метод Reset для того, чтобы поместить ManualResetEvent в несигнальное состояние. Этот поток можно понимать как контролирующий ManualResetEvent. Потоки, которые вызывают метод WaitOne в ManualResetEvent, будут заблокированы, ожидая сигнала. Когда контролирующий поток завершит работу, он вызовет метод Set для сообщения о том, что ожидающие потоки могут продолжить работу. Все ожидающие потоки освобождаются. Когда это было сообщено, ManualResetEvent остается в сигнальном состоянии до того момента, как оно будет снова установлено вручную. То есть, вызовы к WaitOne немедленно возвращаются. Можно контролировать начальное состояние ManualResetEvent, передав конструктору логическое значение, значение true, если начальное состояние сигнальное, и false в противном случае. Иерархия: System.Object Определение: [C#] public sealed class AutoResetEvent : WaitHandle Потокобезопасность: Этот тип можно использовать в многопоточных операциях. Close При переопределении в производном классе освобождает все ресурсы, занимаемые текущим объектом WaitHandle. Reset Задает несигнальное состояние указанного события. Set Задает сигнальное состояние указанного события. WaitOne Блокирует текущий поток до получения сигнала текущим объектом WaitHandle. Определение: public virtual void Close(); public bool Reset(); public bool Set(); public virtual bool WaitOne(); Пример использования классов AutoResetEvent и ManualResetEvent. Их отличие состоит в том, что для класса ManualResetEvent после подачи им сигнала нужно вручную сбрасывать его состояние в false (то есть вызывать метод Reset), для AutoResetEvent это происходит автоматически (как только сработал один из статических методов WaitAny, WaitAll или WaitOne). Это демонстрируется в ниже – приведенном примере (более глубоко изучить данную тему можно в [8]) using System; using System.Threading; class CalculateTest { static void Main() { //Объект вычислений Calculate calc = new Calculate(); //Вывод результатов вычислений Console.WriteLine("Result = {0}.", calc.Result(234).ToString()); Console.WriteLine("Result = {0}.", calc.Result(55).ToString()); Console.Read(); } } class Calculate { double baseNumber, firstTerm, secondTerm, thirdTerm; AutoResetEvent[] autoEvents; ManualResetEvent manualEvent; // Генерация случайных чисел для имитации вычислений Random randomGenerator; //Конструктор (инициализация объектов AutoResetEvent //и ManualResetEvent) public Calculate() { autoEvents = new AutoResetEvent[] { new AutoResetEvent(false), new AutoResetEvent(false), new AutoResetEvent(false) }; manualEvent = new ManualResetEvent(false); } void CalculateBase(object stateInfo) { //Эмитация вычислений baseNumber = randomGenerator.NextDouble(); // Сигнал о том что baseNumber подсчитан manualEvent.Set(); } //Следующие три метода аналогичны по своей сути void CalculateFirstTerm(object stateInfo) { // Вычисляем preCalc double preCalc = randomGenerator.NextDouble(); // Ожидаем вычисления baseNumber manualEvent.WaitOne(); // Вычисляем первый терм(нужны preCalc и baseNumber). firstTerm = preCalc * baseNumber * randomGenerator.NextDouble(); // Сигнал о том что первый терм вычислен autoEvents[0].Set(); } void CalculateSecondTerm(object stateInfo) { double preCalc = randomGenerator.NextDouble(); manualEvent.WaitOne(); secondTerm = preCalc * baseNumber * randomGenerator.NextDouble(); autoEvents[1].Set(); } void CalculateThirdTerm(object stateInfo) { double preCalc = randomGenerator.NextDouble(); manualEvent.WaitOne(); thirdTerm = preCalc * baseNumber * randomGenerator.NextDouble(); autoEvents[2].Set(); } public double Result(int seed) { randomGenerator = new Random(seed); // Одновременное вычисления термов // Запускаем соответствующие методы(все // метожы запускаються в фоновых потоках) ThreadPool.QueueUserWorkItem( new WaitCallback(CalculateBase)); ThreadPool.QueueUserWorkItem( new WaitCallback(CalculateFirstTerm)); ThreadPool.QueueUserWorkItem( new WaitCallback(CalculateSecondTerm)); ThreadPool.QueueUserWorkItem( new WaitCallback(CalculateThirdTerm)); // Ожидаем вычисления всех трех термов // (всех трех сигналов от autoEvents) WaitHandle.WaitAll(autoEvents); // Сбрасываем сигнал manualEvent на false // для следующего вычисления тройки термов // (сигналы autoEvents сбрасываються автоматически) manualEvent.Reset(); return firstTerm + secondTerm + thirdTerm; } } Выходная информация: Result = 0,64236347437457457 Result = 0,25234563246456456 Описание: Предоставляет механизм для синхронизации доступа к объектам. Класс Monitor контролирует доступ к объектам, предоставляя блокировку объекта одному потоку. Блокировки объектов предоставляют возможность ограничения доступа к части кода, обычно называемой критической секцией. Пока поток владеет блокировкой для объекта, никакой другой поток не может ею завладеть. Можно также использовать Monitor для того, чтобы убедиться, что ни один поток не имеет доступа к секции кода приложения, выполняющейся владельцем блокировки, пока другой поток не будет выполнять код, используя другой объект с блокировкой. Следует использовать класс Monitor для блокировки объектов (т. е. ссылочные типы, а не скалярные). Подробные сведения см. в методе Enter и основном разделе Monitor. Monitor имеет следующие свойства: Связывается с объектом по требованию. Он несвязан, что означает, что он может быть вызван непосредственно из любого контекста. Невозможно создать экземпляр класса Monitor. Следующая информация хранится для каждого синхронизированного объекта: Ссылка на поток, который в данный момент владеет блокировкой. Ссылка на очередь готовности, которая содержит потоки, готовые получить блокировку. Ссылка на очередь ожидания, содержащую потоки, ожидающие уведомления об изменении состояния объекта с блокировкой. Иерархия: System.Object Определение: [C#] public sealed class Monitor Потокобезопасность: Этот тип можно использовать в многопоточных операциях. Enter Получает эксклюзивную блокировку указанного объекта. Exit Освобождает эксклюзивную блокировку указанного объекта. Pulse Уведомляет поток в очереди готовности об изменении состояния объекта с блокировкой. PulseAll Уведомляет все ожидающие потоки об изменении состояния объекта. TryEnter Пытается получить эксклюзивную блокировку указанного объекта. Wait Освобождает блокировку объекта и блокирует текущий поток до тех пор, пока тот не получит блокировку снова. Примеры: Демонстрация использования метода Pulse using System; using System.Threading; using System.Collections; namespace MonitorCS1 { class MonitorSample { const int MAX_LOOP_TIME = 1000; Queue m_smplQueue; public MonitorSample() { m_smplQueue = new Queue(); } public void FirstThread() { int counter = 0; lock (m_smplQueue) { while (counter < MAX_LOOP_TIME) { //Ждем,очередб занята Monitor.Wait(m_smplQueue); //Добавляем элемент m_smplQueue.Enqueue(counter); //Убираем ожтдающий поток Monitor.Pulse(m_smplQueue); counter++; } } } public void SecondThread() { lock (m_smplQueue) { //Убираем ожтдающий поток Monitor.Pulse(m_smplQueue); //Ждем в цикле пока очередь не освободиться //Выход по таймауту когда первый поток остановиться while (Monitor.Wait(m_smplQueue, 1000)) { //Вытаскиваем первый элемент int counter = (int)m_smplQueue.Dequeue(); //Вывод первого элемента на консоль Console.WriteLine(counter.ToString()); //Убираем ожтдающий поток Monitor.Pulse(m_smplQueue); } } } //Возвращает число элементов в очереди public int GetQueueCount() { return m_smplQueue.Count; } static void Main(string[] args) { //Создаем объект MonitorSample MonitorSample test = new MonitorSample(); //Создаем первый поток Thread tFirst = new Thread(new ThreadStart(test.FirstThread)); //Создаем второй поток Thread tSecond = new Thread(new ThreadStart(test.SecondThread)); //Запскаем потоки tFirst.Start(); tSecond.Start(); //Ожидаем окончания обоих потоков tFirst.Join(); tSecond.Join(); //Печатаем число элементов в очереди Console.WriteLine("Queue Count = " + test.GetQueueCount().ToString()); } } } Описание: Фиксирует число потоков которые могут обращаться к ресурсам одновременно. (Более подробную информацию можно найти в [8]) Иерархия: System.Object Определение: [C#] [ComVisibleAttribute(false)] public sealed class Semaphore : WaitHandle Потокобезопасность: Этот тип можно использовать в многопоточных операциях. Name Description Handle Получает или устанавливает низкоуровневый handle операционной систем Name Description Close Освобождает все ресурсы использованные WaitHandle GetAccessControl Позволяет получить безопасный доступ к именнованому объекту семафор OpenExisting Открывает существующий именнованный семафор Release Выход из семафора SetAccessControl Устанавливает доступ к именнованому объекту семафор SignalAndWait Сигнализирует одному WaitHandle и ждет другого WaitAll Ждет все элементы в специальном массиве для приема сигнала WaitAny Ждет какой-либо элемент в специальном массиве для приема сигнала WaitOne Блокирует текущий поток до тех пор текущий WaitSignal не получит сигнал. Примеры: Демонстрация использования метода WaitAny класса Semaphore. Пример является фрагментом более объемной программы static void Main() { //Очередь двух задач в двух различных потоках //Ждем завершения обеих задач DateTime dt = DateTime.Now; Console.WriteLine("Main thread is waiting for BOTH tasks to complete."); ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[0]); ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[1]); WaitHandle.WaitAll(waitHandles); // Время от более долгой задачи //Вывод на консоль Console.WriteLine("Both tasks are completed (time waited={0})", (DateTime.Now - dt).TotalMilliseconds); //Очередь двух задач в двух различных потоках //Ждем завершения какой-либо из задач dt = DateTime.Now; Console.WriteLine(); Console.WriteLine("The main thread is waiting for either task to complete."); ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[0]); ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[1]); int index = WaitHandle.WaitAny(waitHandles); // Время от наикротчайшей задачи //Вывод на консоль Console.WriteLine("Task {0} finished first (time waited={1}).", index + 1, (DateTime.Now - dt).TotalMilliseconds); } Пример демонстрирующий использования класса Semaphore using System; using System.Threading; public class Example { //Демонстрация доступа к пулу ресурсов private static Semaphore _pool; //Интервал для более последовательного вывода на консоль private static int _padding; public static void Main() { //Создаем семафор который может разрешить //до трех конкурирующих задач. Использование // нуля, показывает что семафор находиться в //главном(main) потоке программы _pool = new Semaphore(0, 3); //Создаем и запускаем пять потокав for (int i = 1; i <= 5; i++) { Thread t = new Thread(new ParameterizedThreadStart(Worker)); //Запускаем поток соответствующий номеру t.Start(i); } //Ждем пол-секунды, чтобы позваолить потокам запуститься //и быть заблокироваными семафором Thread.Sleep(500); Console.WriteLine("Main thread calls Release(3)."); //Позволяем ожидающим потокам стартовать через // семафор, до трех раз _pool.Release(3); //Выход из главного потока Console.WriteLine("Main thread exits."); } private static void Worker(object num) { //Каждый рабочий поток запускаеться по запросу //семафора Console.WriteLine("Thread {0} begins " + "and waits for the semaphore.", num); _pool.WaitOne(); // Интервал для более последовательного вывода на консоль int padding = Interlocked.Add(ref _padding, 100); Console.WriteLine("Thread {0} enters the semaphore.", num); //Работа каждого из потоков – спать секунду // и немного еще для наглядного вывода на консоль Thread.Sleep(1000 + padding); //Вывод на консоль Console.WriteLine("Thread {0} releases the semaphore.", num); Console.WriteLine("Thread {0} previous semaphore count: {1}", num, _pool.Release()); } } Richard Gerber, Andrew Binstock. Programming with Hyper – Threading Technology MSDN – magazine (статьи связанные с пространством имен System.Threading) Документация MSDN Э.Троелсен. Язык программирования C# и платформа .NET Ресурс http:// msdn.microsoft.com Джеффри Рихтер. CLR via C# Билл Вагнер. Эффективное использование C# Джеффри Рихтер. Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows (На мой взгляд великолепная книга, очень помогла в написании данной работы)Класс WaitHandle
Общая информация.
System.MarshalByRefObject
System.Threading.WaitHandle
System.Threading.AutoResetEvent
System.Threading.ManualResetEvent
System.Threading.Mutex
WaitHandle – составляющие
Поля
Свойства
Методы
Класс Mutex
Общая информация.
System.MarshalByRefObject
System.Threading.WaitHandle
System.Threading.MutexMutex – составляющие
Свойства
Методы
Примеры
Класс AutoResetEvent
Общая информация.
System.MarshalByRefObject
System.Threading.WaitHandle
System.Threading.AutoResetEventAutoResetEvent – составляющие
Методы
Примеры
Класс ManualResetEvent
Общая информация.
System.MarshalByRefObject
System.Threading.WaitHandle
System.Threading.ManualResetEventManualResetEvent – составляющие
Методы
Примеры
Класс Monitor
Общая информация.
System.Threading.MonitorMonitor – составляющие
Методы
Класс Semaphore
Общая информация.
System.MarshalByRefObject
System.Threading.WaitHandle
System.Threading.SemaphoreSemaphore – составляющие
Свойства
Методы
Список литературы