Скачать презентацию
Идет загрузка презентации. Пожалуйста, подождите
Презентация была опубликована 11 лет назад пользователемКлавдия Юдина
1 Windows API для управления задачами
2 Процессы и потоки в Windows Процесс есть объект, обладающий собственным независимым виртуальным адресным пространством, в котором могут размещаться код и данные, защищенные от других процессов. Внутри каждого процесса могут независимо выполняться один или несколько потоков. Поток может создавать новые потоки и новые независимые процессы, управлять взаимодействием объектов между собой и их синхронизацией.
3 Процессы и потоки в Windows Именно поток является базовой единицей выполнения в Windows. Выполнение потоков планируется системой на основе обычных факторов: наличие таких ресурсов, как CPU и физическая память, приоритеты, равнодоступность ресурсов и гак далее. Начиная с Windows NT4 поддерживается симметричная многопроцессорная обработка (Symmetric Multiprocessing, SMP), позволяющая распределять выполнение потоков между отдельными процессорами.
4 Процессы и потоки в Windows Каждому процессу принадлежат следующие ресурсы: Один или несколько потоков. Виртуальное адресное пространство, отличное от адресных пространств других процессов. Один или несколько сегментов кода, включая код DLL. Один или несколько сегментов данных, содержащих глобальные переменные. Строки, содержащие информацию об окружении, например, информацию о текущем пути доступа к файлам. Куча памяти процесса.
5 Процессы и потоки в Windows Различного рода ресурсы, например, дескрипторы открытых файлов. Поток разделяет вместе с процессом код, глобальные переменные, строки окружения и другие ресурсы. Каждый поток планируется независимо от других и располагает следующими элементами: Стек, используемый для вызова процедур, прерываний и обработчиков исключений, а также хранения автоматических переменных. Локальные области хранения потока (Thread Local Storage, TLS) массивы указателей, используя которые каждый поток может создавать собственную уникальную информационную среду.
6 Процессы и потоки в Windows Аргумент в стеке, получаемый от создающего потока, который обычно является уникальным для каждого потока. Структура контекста, поддерживаемая ядром системы и содержащая значения машинных регистров. На рисунке ниже показан процесс с несколькими потоками
7 Процессы и потоки в Windows
8 Создание процесса Функция CreateProcess создает новый процесс с единственным потоком. При вызове этой функции требуется указать имя файла исполняемой программы. Для большинства параметров можно использовать значения, заданные по умолчанию (NULL). BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpsaProcess, LPSECURITY_ATTRIBUTES lpsaThread, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurDir, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcInfo);
9 Создание процесса Функция возвращает два дескриптора, по одному для процесса и потока, передавая их в структуре типа PPROCESS_INFORMATION. Эти дескрипторы относятся к создаваемому функцией CreateProcess новому процессу и его основному потоку. Во избежание утечки ресурсов в процессе работы необходимо закрывать оба дескрипторов, когда они больше не нужны. Возвращаемое значение: в случае успешного создания процесса и потока - TRUE, иначе - FALSE.
10 Создание процесса lpApplicationName и lpCommandLine используются вместе для указания исполняемой программы и аргументов командной строки. При этом действуют следующие правила: Указатель lpApplicationName, если его значение не равно NULL, указывает на строку, содержащую имя файла исполняемого модуля. Если имя модуля содержит пробелы, его заключают в кавычки. Если значение указателя lpApplicationName равно NULL, то имя модуля определяется первой из лексем в параметре lpCommandLine.
11 Создание процесса Обычно задается только параметр lpCommandLine, в то время как параметр lpApplicationName полагается равным NULL. Новый процесс может получить командную строку посредством обычного arqv-механизма или путем вызова функции GetCommandLine для получения командной строки в виде одиночной строки символов. lpsaProcess и lpsaThread указатели на структуры атрибутов защиты процесса и потока. Значениям NULL соответствует использование атрибутов защиты, заданных по умолчанию.
12 Создание процесса bInheritHandles режим наследования открытых дескрипторов файлов, и так далее, из вызывающего процесса. Для наследования значение параметра устанавливают равным TRUE, иначе FALSE. dwCreationFlags может объединять в себе несколько флаговых значений, включая следующие: –CREATESUSPENDED - указывает на то, что основной поток будет создан в приостановленном состоянии и начнет выполняться лишь после вызова функция ResumeThread.
13 Создание процесса –Detached_process и Create_new_console - взаимоисключающие флаги: первый означает создание нового процесса, у которого консоль отсутствует, а второй процесса, у которого имеется собственная консоль. Если ни один из этих флагов не указан, то новый процесс наследует консоль родительского процесса. –Create_New_Process_Group указывает на то, что создаваемый процесс является корневым для новой группы процессов. Если все процессы, принадлежащие данной группе, разделяют общую консоль, то все они будут получать управляющие сигналы консоли (Ctrl- C).
14 Создание процесса –флаги управления приоритетами потоков нового процесса. Можно использовать приоритет родительского процесса (устанавливается по умолчанию) или указывать значение NORMAL_PRIORITY_CLASS. lpEnvironment блок параметров настройки окружения нового процесса. Если задано значение NULL, то новый процесс будет использовать значения параметров окружения родительского процесса. lpCurDir указатель на строку, содержащую путь к текущему каталогу нового процесса. Если задано значение NULL, то будет использоваться рабочий каталог родительского процесса.
15 Создание процесса lpStartupInfo указатель на структуру, которая описывает внешний вид основного окна и содержит дескрипторы стандартных устройств нового процесса. Соответствующую информацию из родительского процесса можно получить при помощи функции GetStartupInfo. Можно обнулить структуру STARTUPINFO перед вызовом функций CreateProcess. lpProInfо указатель на структуру, в которую помещаются значения дескрипторов и идентификаторов процесса и потока.
16 Создание процесса Структура PROCESS_INFORMATION имеет следующий вид: typedef struct PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadld;} PROCESS_INFORMATION; Процессам и потокам нужны и дескрипторы, и идентификаторы, поскольку одним функциям управления требуются идентификаторы, а другим дескрипторы. Дескрипторы процессов и потоков должны закрываться после того, как необходимость в них отпала.
17 Завершение и прекращение выполнения процесса После того как процесс завершил свою работу, он может вызвать функцию ExitProcess, указав в качестве параметра код завершения: VOID ExitProcess (UINT uExitCode) Эта функция не осуществляет возврата. Она завершает вызывающий процесс и все его потоки. Выполнение оператора return в основной программе с использованием кода возврата равносильно вызову функции ExitProcess, в котором этот код возврата указан в качестве кода завершения. Другой процесс может определить код завершения, вызвав функцию GetExitCodeProcess: BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode)
18 Завершение и прекращение выполнения процесса lpExitCode указывает на переменную типа DWORD, вторая принимает значение кода завершения. Одним из ее возможных значений является STILL_ACTIVE, означающее, что данный процесс еще не завершился. Один процесс может прекратить выполнение другого процесса, если у дескриптора завершаемого процесса имеются права доступа PROCESS_TERMINATE. При вызове функции завершения процесса указывается код завершения: BOOL TerminateProcess HANDLE hProcess, UINT uExitCode) Прежде чем завершить выполнение процесса, все ресурсы, которые он мог разделять с другими процессами, должны быть освобождены.
19 Ожидание завершения процесса Простейшим методом синхронизации с другим процессом является ожидание его завершения. Представленные ниже стандартные функции ожидания Windows обладают рядом интересных свойств. Функции ожидания могут работать с самыми различными типами объектов (процессами и потоками). Функции могут ожидать завершения одного процесса, первого из нескольких указанных, или всех процессов, образующих группу. Есть возможность устанавливать конечный интервал ожидания.
20 Ожидание завершения процесса DWORD WaitForSingleObject (HANDLE hObject, DWORD dwMilliseconds) DWORD WaitForMultipleObjects (DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMilliseconds) Возвращаемое значение указывает причину завершения ожидания или, в случае ошибки, равно 0xFFFFFFFF (для получения более подробной информации используйте функцию GetLastError).
21 Ожидание завершения процесса В аргументах этих функций указывается либо дескриптор одиночного процесса (hObject), либо дескрипторы ряда отдельных объектов, хранящиеся в массиве, на который указывает указатель lpHandles. Значение параметра nCount, определяющего размер массива, не должно превышать 64 (определено в файле WINNT.Н). dwMilliseconds число миллисекунд интервала ожидания. Если число равно 0, то возврат из функции осуществляется сразу же, что полезно при опросе состояния процессов. Если же значение параметра равно INFINITE, то ожидание длится до завершения процесса. fWaitAll параметр, указывающий (если его значение равно TRUE) на необходимость ожидания завершения всех процессов.
22 Ожидание завершения процесса Возможные возвращаемые значения функций: WAIT_OBJECT_0 указанный объект завершен или остановлен (в случае функции WaitForSingleObject) или что одновременно все nCount объектов завершены или остановлены (в случае функции WaitForMultipleObject, когда параметр fWaitAll равен TRUE). WAIT_OBJECT_0+n, где 0 < n < nCount вычтя значение WAIT_OBJECT_0 из возвращенного значения, можно определить, выполнение какого процесса завершилось, если ожидается завершение выполнения любого из группы процессов. Если завершено несколько объектов, возвращается наименьшее из возможных значений.
23 Ожидание завершения процесса WAIT_TIMEOUT указывает на то, что в течение отведенного периода ожидания объект (объекты) не завершены. WAIT_FAILED неудачное завершение функции, вызванное, например, тем, что у дескриптора отсутствовали права доступа. Программа spaces_new.cpp иллюстрирует применение методики, обеспечивающей взаимодействие между процессами.spaces_new.cpp
24 Многопоточное программирование Ранее было показано, каким образом обеспечивается существование потоков в среде процесса. Использование потоков на примере многопоточного сервера, способного обрабатывать запросы одновременно нескольких клиентов, иллюстрирует рисунок ниже. Потоки, принадлежащие одному процессу, разделяют общие данные и код, поэтому важно, чтобы каждый поток имел также собственную область памяти. Удовлетворение этого требования обеспечивается несколькими способами: –У каждого потока имеется собственный стек, который она использует при вызове функций и обработке некоторых данных. –При создании потока вызывающий процесс может передать ему аргумент (Arg на рисунке), как правило - указатель. Этот аргумент помещается в стек потока.
25 Многопоточное программирование
26 –Каждый поток может считывать и устанавливать значения локальных областей хранения (Thread Local Storage, TLS). TLS предоставляют в распоряжение потоков небольшие массивы данных. TLS обеспечивают защиту данных одного потока, от воздействия со стороны других потоков. Для создания потоков в адресном пространстве процесса, предусмотрен системный вызов CreateThread. Он требует указания: начального адреса потока в коде процесса; размера стека (из виртуального адресного пространства процесса), по умолчанию равен размеру стека основного потока; указателя на аргумент, передаваемый потоку.
27 Многопоточное программирование Функция возвращает значение идентификатора (ID) и дескриптор потока. В случае ошибки возвращаемое значение равно NULL. HANDLE CreateThread (LPSECURITY_ATTRIBUTES lpsa, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddr, LPVOID lpThreadParm,DWORD dwCreationFlags,LPDWORD lpThreadId); Параметры lpsa указатель на структуру атрибутов защиты. dwStackSize размер стека нового потока в байтах. Значению 0 соответствует размер стека по умолчанию.
28 Многопоточное программирование lpStartAddr указатель на функцию (в коде процесса), которая должна выполняться. Функция принимает единственный аргумент в виде указателя и возвращает 32-битовый код завершения. Функция потока (ThreadFunc) имеет сигнатуру: DWORD WINAPI ThreadFunc (LPVOID); lpThreadParm указатель, передаваемый в функцию потока. dwCreationFlags если значение этого параметра установлено равным 0, то поток запускается сразу после вызова функции CreateThread. Установка значения CREATE_SUSPENDED приведет к запуску потока в приостановленном состоянии, из которого поток может быть переведен в состояние готовности вызовом функции ResumeThread.
29 Многопоточное программирование lpThreadId указатель на переменную типа DWORD, которая получает идентификатор нового потока. Поток процесса может завершить свое выполнение, вызвав функцию ExitThread, но обычным способом завершения потока является возврат из функции потока с использованием кода завершения в качестве возвращаемого значения. По завершении выполнения потока память, занимаемая его стеком, освобождается. Когда завершается выполнение последнего потока, завершается и выполнение процесса. VOID ExitThread (DWORD dwExitCode);
30 Многопоточное программирование Выполнение потока также может быть завершено другим потоком с помощью функции TerminateThread, однако освобождения ресурсов потока при этом не происходит. Поток, выполнение которого было завершено, продолжает существовать до тех пор, пока посредством функции CloseHandle не будет закрыт его последний дескриптор. Любой другой поток может получить код завершения потока: BOOL GetExitCodeThread (HANDLE hThread, LPDWORD lpExitCode). Если поток еще не завершен, то значение этой переменной будет равно STILL_ACTIVE.
31 Многопоточное программирование Функции для получения идентификаторов и дескрипторов потоков, напоминают те, которые используются в случае процессов: GetCurrentThread возвращает ненаследуемый псевдодескриптор вызывающего потока. GetCurrentThreadId возвращает идентификатор потока. GetThreadId возвращает идентификатор потока по дескриптору. OpenThread создает дескриптор потока по идентификатору.
32 Многопоточное программирование Для каждого потока поддерживается счетчик приостановок (suspend count), и выполнение потока может быть продолжено, если значение этого счетчика равно 0. Поток может увеличивать или уменьшать значение счетчика приостановок другого потока функциями SuspendThread и ResumeThread: DWORD ResumeThread (HANDLE hThread); DWORD SuspendThread (HANDLE hThread) В случае успешного выполнения обе функции возвращают предыдущее значение счетчика приостановок, иначе 0xFFFFFFFF. Поток может дожидаться завершения выполнения другого потока. При вызове функций ожидания (WaitForSingleObject и WaitForMultipleObjects) следует использовать дескрипторы потоков.
33 Многопоточное программирование Допустимое количество объектов, одновременно ожидаемых функцией WaitForMultipleObjects, ограничено значением MAXIMUM_WAIT_OBJECTS (64), но при большом количестве потоков можно воспользоваться серией вызовов функций ожидания. Функция ожидания дожидается, пока объект, указанный дескриптором, не перейдет в сигнальное состояние. Поток переводится в сигнальное состояние при помощи функций ExitThread и TerminateThread. Функция ExitProcess переводит в сигнальное состояние как сам процесс, так и все его потоки. Пример многопоточного приложения – winthread.cppwinthread.cpp
34 Средства синхронизации потоков Windows предоставляет четыре объекта, предназначенных для синхронизации потоков и процессов. Три из них мьютексы, семафоры и события являются объектами ядра, имеющими дескрипторы. Четвертый объект критические участки кода (CRITICAL_SECTION). Последние являются предпочтительным механизмом. Объект взаимного исключения (mutual exception), или мьютекс (mutex), обеспечивает более универсальную функциональность по сравнению с объектом CRITICAL_SECTION. Поскольку мьютексы могут иметь имена и дескрипторы, их можно использовать также для синхронизации потоков, принадлежащих различным процессам.
35 Средства синхронизации потоков Поток приобретает права владения мьютексом (блокирует мьютекс) путем вызова функции ожидания (WaitForSingleObject или WaitForMultipleObjects) по отношению к дескриптору мьютекса и уступает эти права посредством вызова функции ReleaseMutex. Поток может завладевать одним и тем же ресурсом несколько раз, и при этом не будет блокироваться, если уже владеет данным ресурсом. Поток должен освободить мьютекс столько раз, сколько он его захватывал. Возможность рекурсивного захвата ресурсов полезна для ограничения доступа к рекурсивным функциям.
36 Средства синхронизации потоков При работе с мьютексами используются функции CreateMutex, ReleaseMutex и OpenMutex. HANDLE CreateMutex (LPSECURITY_ATTRIBUTES lpsa, BOOL bInitialOwner, LPCTSTR lpMutexName) bInitialOwner если равно True, то вызывающий поток немедленно приобретает права владения новым мьютексом. Эта атомарная операция предотвращает приобретение прав владения мьютексом другими потоками, прежде чем это сделает поток, создающий мьютекс. Флаг не оказывает никакого действия, если мьютекс уже существует.
37 Средства синхронизации потоков lpMutexName указатель на строку, содержащую имя мьютекса; в отличие от файлов имена мьютексов чувствительны к регистру. Если этот параметр равен NULL, то мьютекс создается без имени. События, мьютексы, семафоры и другие объекты ядра используют одно пространство имен, отличное от пространства имен файловой системы. Поэтому имена всех объектов синхронизации должны быть различными. Длина имен объектов не может превышать 260 символов. Возвращаемое значение имеет тип HANDLE; значение NULL указывает на неудачное завершение функции.
38 Средства синхронизации потоков HANDLE OpenMutex(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName); Функция OpenMutex открывает существующий именованный мьютекс, что дает возможность потокам, принадлежащим различным процессам, синхронизироваться, как если бы они принадлежали одному процессу. Вызову функции OpenMutex в одном процессе должен предшествовать вызов функции CreateMutex в другом процессе. Функция ReleaseMutex освобождает мьютекс. Если мьютекс не принадлежит потоку, функция завершается с ошибкой. BOOL ReleaseMutex (HANDLE hMutex);
39 Средства синхронизации потоков Мьютекс, владевший которым поток завершился, не освободив его, называют покинутым. На то, что дескриптор представляет собой покинутый мьютекс, указывает возврат функцией WaitForSingleObject значения WAIT_ABANDONED_0 или использование значения WAIT_ABANDONED_0 в качестве базового значения функцией WaitForMultipleObject. Рассмотрим пример многопоточного приложения с мьютексами на примере задачи производителей- потребителей, известной также как задача писателей-читателей. Схема задачи представлена на следующем слайде. Особенность ее в том, что производители вступают в конкуренцию за глобальный ресурс.
40 Средства синхронизации потоков
41 Целочисленный массив buff содержит производимые и потребляемые данные (данные совместного пользования) Для простоты производители просто устанавливают значение buff[0] в 0, buff[1] в 1 и т. д. Потребитель перебирает элементы массива, проверяя правильность записей. В этом примере требуется синхронизация между отдельными потоками-производителями. Поток- потребитель не будет запущен, пока все производители не завершат свою работу. В файле prodcons.c приведен текст примера. prodcons.c Совместно используемые потоками переменные объединяются в структуру с именем shared вместе с взаимным исключением, чтобы подчеркнуть, что доступ к ним можно получить только вместе с ним.
42 Средства синхронизации потоков Переменная nput хранит индекс следующего элемента массива buff, подлежащего обработке, a nval содержит следующее значение, которое должно быть в него помещено (0, 1, 2 и т. д.). Первый аргумент командной строки указывает количество элементов, которые будут произведены производителями, а второй количество запускаемых потоков-производителей. Создаются потоки-производители, каждый из которых вызывает функцию produce. Идентификаторы потоков хранятся в массиве tid_produce. Аргументом каждого потока- производителя является указатель на элемент массива count. Счетчики инициализируются значением 0, и каждый поток увеличивает значение своего счетчика на 1 при помещении очередного элемента в буфер.
43 Средства синхронизации потоков Содержимое массива счетчиков затем выводится на экран, так что можно узнать, сколько элементов было помещено в буфер каждым из потоков. Программа ожидает завершения работы всех потоков-производителей, выводя содержимое счетчика для каждого потока, а затем запускат единственный процесс-потребитель. Таким образом исключается необходимость синхронизации между потребителем и производителями. Программа ждет завершения работы потребителя, а затем завершает работу процесса. Критическая область кода производителя состоит из проверки на достижение конца массива (завершение работы)
44 Средства синхронизации потоков if (shared.nput >= nitems) и трех строк, помещающих очередное значение в массив: shared.buff[shared.nput]= shared.nval; shared.nput++; shared.nval++: Эта область защищается с помощью взаимного исключения, которое освобождается после завершения работы. Потребитель проверяет правильность значений всех элементов массива и выводит сообщение в случае обнаружения ошибки. Это можно проверить, загрузив исполняемый файл программы.файл
45 Средства синхронизации потоков Если убрать из этого примера блокировку с помощью взаимного исключения, он перестанет работать, как и предполагается. Потребитель обнаружит множество элементов buff[i], значения которых будут отличны от i. Другой вариант его работы - несколько производителей будут формировать одни и те же элементы данных.убрать Также можно убедиться, что удаление блокировки ничего не изменит, если будет выполняться только один поток.
46 Средства синхронизации потоков Если же запускать поток-потребитель сразу после запуска всех потоков-производителей, то возникнет проблема что ему делать, если нужный элемент еще не готов. Можно повторять операции в цикле, устанавливая и снимая блокировку и проверяя значение индекса. Это называется опросом ( spinning или polling ) и является лишней тратой времени процессора. Можно было бы приостановить выполнение процесса на некоторое время, но не известно, на какое. Поэтому нужно использовать какое-то другое средство синхронизации, позволяющее потоку или процессу приостанавливать работу, пока не произойдет какое-либо событие.
47 Средства синхронизации потоков Одним из рассматриваемых типов объектов синхронизации ядра являются события (events). Объекты события используются для того, чтобы сигнализировать другим потокам о наступлении какого-либо события, например, о появлении нового сообщения.
48 Средства синхронизации потоков Важная особенность событий заключается в том, что переход в сигнальное состояние единственного события способен вывести из состояния ожидания одновременно несколько потоков. Объекты события делятся на сбрасываемые вручную и автоматически, и это их свойство устанавливается при вызове функции CreateEvent. Сбрасываемые вручную события (manual-reset events) могут сигнализировать одновременно всем потокам, ожидающим наступления этого события, и переводятся в несигнальное состояние программно.
49 Средства синхронизации потоков Автоматически сбрасываемые события (auto-reset event) сбрасываются самостоятельно после освобождения одного из ожидающих потоков, тогда как другие ожидающие потоки продолжают ожидать перехода события в сигнальное состояние. События используют пять функций: CreateEvent, OpenEvent, SetEvent, ResetEvent и PulseEvent. HANDLE CreateEvent (LPSECURITY_ATTRIBUTES lpsa, BOOL bManualReset, BOOL bInitialState, LPTCSTR lpEventName); Чтобы создать событие, сбрасываемое вручную, необходимо установить значение параметра bManualReset равным True.
50 Средства синхронизации потоков Чтобы сделать начальное состояние события сигнальным, установите равным True значение параметра bInitialState. Для открытия именованного объекта события используется функция OpenEvent, причем это может сделать и другой процесс. Для управления объектами событий используются три функции: BOOL SetEvent (HANDLE hEvent); BOOL ResetEvent (HANDLE hEvent); BOOL PulseEvent (HANDLE hEvent); Поток может установить событие в сигнальное состояние, используя функцию SetEvent.
51 Средства синхронизации потоков Если событие является автоматически сбрасываемым, то оно возвращается в несигнальное состояние после освобождения только одного из ожидающих потоков. В отсутствие потоков, ожидающих наступления этого события, оно остается в сигнальном состоянии, пока такой поток не появится, после чего этот поток сразу же освобождается. Таким же образом ведет себя семафор, максимальное значение счетчика которого установлено равным 1. Если событие является сбрасываемым вручную, то оно остается в сигнальном состоянии, пока какой- либо поток не вызовет функцию ResetEvent, указав дескриптор этого события в качестве аргумента.
52 Средства синхронизации потоков Все ожидающие потоки освобождаются, но до выполнения такого сброса события другие потоки могут как переходить в состояние его ожидания, так и освобождаться. Функция PulseEvent освобождает все потоки, ожидающие наступления сбрасываемого вручную события, но после этого событие сразу же автоматически сбрасывается. В случае же использования автоматически сбрасываемого события функция PulseEvent освобождает только один ожидающий поток, если таковые имеются.
53 Средства синхронизации потоков Функция PulseEvent становится полезной после того, как сбрасываемое вручную событие установлено в сигнальное состояние с помощью функции SetEvent. При использовании функции WaitForMultipleObjects ожидающий поток освободится, когда одновременно все события будут находиться в сигнальном состоянии, но некоторые из событий, находящихся в сигнальном состоянии, могут быть сброшены, прежде чем поток освободится. Пример применения мьютексов и событий – программа eventPC.c, реализующая систему «производитель/потребитель», и доступная на сайте в разделе «WinAPI».eventPC.c
Еще похожие презентации в нашем архиве:
© 2024 MyShared Inc.
All rights reserved.