Добавлен: 26.10.2023
Просмотров: 53
Скачиваний: 4
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
Министерство науки и высшего образования РФ
ФГАОУ ВПО
Национальный исследовательский технологический университет «МИСиС»
Институт Информационных технологий и компьютерных наук (ИТКН)
Кафедра Инфокоммуникационных технологий (ИКТ)
Реферат
по дисциплине «Программирование и алгоритмизация»
на тему «Синхронизация потоков. Мониторы.»
Выполнил:
студент группы БИВТ-22-12
Самойленко А.И.
Проверил:
Стучилин В.В.
Москва, 2022
1.Введение.
Потоки. Синхронизация потоков.
Поток данных в программировании — абстракция, используемая для чтения или записи файлов, сокетов и т.п. в единой манере.
Потоки являются удобным унифицированным программным интерфейсом для чтения или записи файлов, сокетов и передачи данных между процессами.
Поддержка потоков включена в большинство языков программирования и едва ли не во все современные.
При запуске процесса ему предоставляются предопределённые стандартные потоки.
Возможность перенаправления потоков позволяет связывать различные программы, и придаёт системе гибкость.
Мониторы.
Монитор — в языках программирования, высокоуровневый механизм взаимодействия и синхронизации процессов, обеспечивающий доступ к неразделяемым ресурсам. Подход к синхронизации двух или более компьютерных задач, использующих общий ресурс, обычно аппаратуру или набор переменных.
При многозадачности, основанной на мониторах, компилятор или интерпретатор прозрачно для программиста вставляет код блокировки-разблокировки в соответствующим образом оформленные процедуры, избавляя программиста от явного обращения к примитивам синхронизации.
Изобретён Пером Бринчем Хансеном, впервые воплощён в языке Concurrent Pascal и использован для структурирования межпроцессного взаимодействия в операционной системе Solo.
Раздел 1.
2. Многопоточность.
Многопоточность — способ выполнения процесса, при котором вычисления разбиваются на несколько потоков, выполняющихся «параллельно», т.е. без предписанного порядка во времени. При выполнении некоторых задач такое разделение может достичь более эффективного использования ресурсов вычислительной машины.
Все потоки выполняются в адресном пространстве процесса.
При необходимости одновременного выполнения нескольких операций в рамках одной прикладной программы на реализацию каждой из них выделяется отдельный поток. Задача предоставления потоку необходимого процессорного времени возлагается исключительно на операционную систему.
При решении вопроса, какому потоку отдать предпочтение и сколько выделить ему времени, операционной системе приходится учитывать немало факторов. Недостаточно просто установить таймер, чтобы каждые несколько миллисекунд управление передавалось следующему очередному потоку.
3. Функции создания потока и его завершения.
Для создания потока можно использовать функцию из стандартной библиотеки языка С, определенную в файле process.h следующим образом и возвращающую дескриптор созданного потока:
unsigned long _beginthread( void( __cdecl *start_address )( void * );
unsigned stack_size, void *arglist );
1.Через первый параметр start_address функции _beginthread передается адрес функции потока. После вызова функции _beginthread код функции потока, так же как и код любой другой функции, которая может быть вызвана из этой функции потока, выполняется одновременно с оставшимся кодом программы.
2.Последний параметр arglist функции _beginthread позволяет потоку, который создает этой функцией другой поток, передавать информацию этому потоку в виде 32-разрядной переменной. Обычно эта переменная является указателем на блок памяти (например, на структуру данных). Это дает возможность создающему и создаваемому потокам совместно владеть информацией без использования глобальных переменных (общая память потоков).
3.Второй параметр stack_size функции _beginthread служит для задания размера стека для потока. Если через этот параметр передать нулевое значение, то операционная система полагает размер стека для потока равным размеру стека главного потока.
Два и более потока могут использовать одну и ту же функцию процесса. В этом случае автоматические переменные функции потока (хранящиеся в стеке) уникальны для каждого потока, все статические переменные этой функции потока являются общими для всех потоков, использующих эту функцию.
Завершение работы потока происходит при вызове функций:
void _endthread( void );
Но вызов этой функций не является строго необходимым, поскольку поток автоматически уничтожается при выходе из функции потока (при завершении ее выполнения), а также при завершении работы всего приложения. Однако функция _endthread является полезной при выходе из потока, находящегося глубоко в иерархии потоков обработки.
4. Стандартные классы потоков ввода вывода
Объект стандартного потока ввода cin из класса istream присоединен к стандартному устройству ввода (обычно клавиатура).
Int k;
cin >> k;
В переменную k запишется целое число, введенное с клавиатуры.
Объект стандартного потока вывода cout класса ostream присоединен к стандартному устройству вывода (обычно экран монитора).
int k;
cout <
На экране монитора выведется число 5.
Операции вывести в поток и взять из потока являются достаточно умными, чтобы оперировать данными различных типов.
Объект cerr класса ostream присоединен к стандартному устройству вывода сообщений об ошибках.
Потоки ввода-вывода имеют методы для ввода и вывода данных. Например, метод put cout позволяет выводить один символ, а метод get cin вводить один символ.
cout.put(‘s’);
char c=cin.get();
-
Методы файловых потоков
+
1.close() - закрывает файл;
2.open(const char* s,ios_base::openmode n, long protection = 0666) - открывает файл;
3.bool is_open() - определяет открыт ли файл.
-
Класс strstream.
Важное свойство класса strstream состоит в том, что в нем автоматически выделяется требуемый объем памяти для хранения строковых данных. Все операции со строковыми потоками происходят в памяти в специально выделенном для них буфере strstreambuf. Строковые потоки позволяют облегчить формирование данных в памяти.
class strstream : public iostream { public: strstream();
strstream(char *s, streamsize n,
ios_base::openmode=ios_base::in | ios_base::out); strstreambuf *rdbuf() const; void freeze(boolfrz=true); char *str();
streamsize pcount() const;
};
7. Пример программы с потоками. С#
class Program
{
static int x=0;
static object locker = new object();
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Thread myThread = new Thread(Count);
myThread.Name = "Поток " + i.ToString();
myThread.Start();
}
Console.ReadLine();
}
public static void Count()
{
lock (locker)
{
x = 1;
for (int i = 1; i < 9; i++)
{
Console.WriteLine("{0}: {1}", Thread.CurrentThread.Name, x);
x++;
Thread.Sleep(100);
}
}
}
}
Здесь у нас запускаются пять потоков, которые работают с общей переменной x. И мы предполагаем, что метод выведет все значения x от 1 до
8. И так для каждого потока.
Здесь так же приведен пример использования полезной операции lock – Оператор определяет блок кода, внутри которого весь код блокируется и становится недоступным для других потоков до завершения работы текущего потока.
Раздел 2.
8. Мониторы. Взаимоисключительность.
Монитор состоит из:
-
набора процедур, взаимодействующих с общим ресурсом -
мьютекса -
переменных, связанных с этим ресурсом -
инварианта, который определяет условия, позволяющие избежать состояние гонки
Процедура монитора захватывает мьютекс перед началом работы и держит его или до выхода из процедуры, или до момента ожидания условия (см. ниже). Если каждая процедура гарантирует, что перед освобождением мьютекса инвариант истиннен, то никакая задача не может получить ресурс в состоянии, ведущем к гонке.
*Мьютекс представляет собой взаимно исключающий синхронизирующий объект. Это означает, что он может быть получен потоком только по очереди. Мьютекс предназначен для тех ситуаций, в которых общий ресурс может быть одновременно использован только в одном потоке.
9. Мониторы. C#
Monitor Класс позволяет синхронизировать доступ к области кода, вызывая и освобождая блокировку конкретного объекта путем вызова Monitor.Enter Monitor.TryEnter методов, и Monitor.Exit . Блокировки объектов предоставляют возможность ограничить доступ к блоку кода, обычно называемому критическим разделом. Пока поток владеет блокировкой объекта, другой поток не может получить эту блокировку. Можно также использовать класс, Monitor чтобы запретить другим потокам доступ к разделу кода приложения, выполняемому владельцем блокировки, если только другой поток не выполняет код, используя другой заблокированный объект.
Monitor имеет следующие возможности.
-
Он связан с объектом по требованию. -
Он не связан. Это означает, что его можно вызывать непосредственно из любого контекста. -
Экземпляр Monitor класса не может быть создан; методы Monitor класса являются статическими. Каждому методу передается синхронизированный объект, который управляет доступом к критической секции.
class Program
{
static int x=0;
static object locker = new object();
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Thread myThread = new Thread(Count);
myThread.Name = $"Поток {i.ToString()}";
myThread.Start();
}
Console.ReadLine();
}
public static void Count()
{
bool acquiredLock = false;
try
{
Monitor.Enter(locker, ref acquiredLock);
x = 1;
for (int i = 1; i < 9; i++)
{
Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
x++;
Thread.Sleep(100);
}
}
finally
{
if(acquiredLock) Monitor.Exit(locker);
}
}
}
Метод Monitor.Enter принимает два параметра - объект блокировки и значение типа bool, которое указывает на результат блокировки (если он равен true, то блокировка успешно выполнена). Фактически этот метод блокирует объект locker так же, как это делает оператор lock. С помощью А в блоке try...finally с помощью метода Monitor.Exit происходит освобождение объекта locker, если блокировка осуществлена успешно, и он становится доступным для других потоков.
Кроме блокировки и разблокировки объекта класс Monitor имеет еще ряд методов, которые позволяют управлять синхронизацией потоков. Так, метод Monitor.Wait освобождает блокировку объекта и переводит поток в очередь ожидания объекта. Следующий поток в очереди готовности объекта блокирует данный объект. А все потоки, которые вызвали метод Wait, остаются в очереди ожидания, пока не получат сигнала от метода Monitor.Pulse или Monitor.PulseAll, посланного владельцем блокировки. Если метод Monitor.Pulse отправил сигнал, то поток, находящийся во главе очереди ожидания, получает сигнал и блокирует освободившийся объект. Если же метод Monitor.PulseAll отправлен, то все потоки, находящиеся в очереди ожидания, получают сигнал и переходят в очередь готовности, где им снова разрешается получать блокировку объекта.
10. Объект Lock.
Класс Monitor состоит из static методов (в C#) или Shared (в Visual Basic), которые работают с объектом, который управляет доступом к критическому разделу. Для каждого синхронизированного объекта сохраняются следующие сведения.
-
Ссылка на поток, который в настоящий момент удерживает блокировку. -
Ссылка на очередь готовности, которая содержит потоки, готовые к получению блокировки. -
Ссылка на очередь ожидания, которая содержит потоки, ожидающие уведомления об изменении состояния заблокированного объекта.
Monitor блокирует объекты (то есть ссылочные типы), а не типы значений. Хотя можно передать тип значения в Enter и Exit, он упаковывается отдельно для каждого вызова. Поскольку при каждом вызове создается отдельный объект, Enter никогда не выполняет блокировку, а код, который он предположительно защищает, на самом деле не синхронизируется. Кроме того, объект, переданный в Exit, отличается от объекта, переданного в Enter, поэтому Monitor вызывает исключение SynchronizationLockException с сообщением «Для не синхронизированного блока кода вызван метод синхронизации объектов».
Приведенный ниже пример иллюстрирует данную проблему. Он запускает десять задач, каждая из которых просто бездействует в течение 250 миллисекунд. Затем каждая задача обновляет переменную счетчика nTasks, который предназначен для подсчета количества фактически запущенных и выполненных задач. Поскольку nTasks является глобальной переменной, которая может обновляться несколькими задачами одновременно, используется монитор, защищающий ее от одновременного изменения несколькими задачами. Тем не менее, как показывают выходные данные в примере, каждая из задач вызывает исключение SynchronizationLockException.