Пример простейшей многопоточной программы на WinAPI
25 декабря 2013
Продолжаем вспоминать Windows API. До сих пор мы писали грустные и унылые однопоточные программки из серии «вызови правильную процедуру с нужными аргументами и посмотри на ее код возврата». Сегодня же мы наконец-то напишем хоть и простенькую, но все же самую что ни на есть многопоточную программу, с теми самыми тредами и мьютексами, которые все постоянно ругают.
Из новых процедур нам понадобятся следующие.
CreateThread ( NULL , 0 , hMutex , 0 , NULL ) ;
Как нетрудно догадаться, CreateThread создает новую нитку. Аргументы слева направо — (1) нечто, о чем сейчас знать не нужно, (2) размер стека в байтах, округляется до размера страницы, если ноль, то берется размер по умолчанию, (3) указатель на процедуру, с которой следует начать выполнение потока, (4) аргумент процедуры, переданной предыдущим аргументом, обычно здесь передается указатель на некую структуру, (5) флаги, например, можно создать приостановленный поток (CREATE_SUSPENDED), а затем запустить его с помощью ResumeThread, (6) куда записать ThreadId созданного потока. В случае успеха процедура возвращает хэндл созданного потока. В случае ошибки возвращается NULL, а подробности можно узнать через GetLastError.
Создание игры на C++ / Змейка — Урок #1 (Создание карты)
CreateMutex ( NULL , FALSE , NULL ) ;
Процедура CreateMutex создает новый мьютекс. О первом аргументе сейчас знать не нужно. Если второй аргумент равен TRUE, созданный мьютекс будет сразу залочен текущим потоком, если же второй аргумент равен FALSE, создается разлоченный мьютекс. Третий аргумент задает имя мьютекса, если мы хотим создать именованный мьютекс.
Именованные мьютексы нам пока что не понадобятся, поэтому передаем NULL. Возвращаемые значения в точности такие же, как и у CreateThread.
WaitForSingleObject ( hHandle , dwTimeout ) ;
WaitForSingleObject ждет, когда объект, хэндл которого был передан первым аргументом, перейдет в сигнальное состояние (signaled state). Кроме того, в зависимости от типа объекта, процедура может менять его состояние. WaitForSingleObject может быть применен к мьютексам, семафорам, потокам, процессам и не только.
Если hHandle представляет собой хэндл мьютекса, процедура ждет, когда мьютекс освободится, а затем лочит его. Если же hHandle является хэндлом потока, то процедура просто ждет его завершения. Второй аргумент задает время ожидания в миллисекундах. Можно ждать вечно, передав специальное значение INFINITE. Если указать ноль, процедура не переходит в режим ожидания, а возвращает управление немедленно.
В случае ошибки процедура возвращает WAIT_FAILED, а подробности поможет узнать GetLastError. В случае успеха возвращается WAIT_OBJECT_0, если мы дождались перехода объекта в сигнальное состояние, и WAIT_TIMEOUT, если отвалились по таймауту. Также мы можем получить WAIT_ABANDONED. Это происходит в случае, если нить, державшая мьютекс, завершилась, не освободив его. В этом случае мьютекс становится залочен текущей нитью, но целостность данных, доступ к которым ограничивался мьютексом, по понятным причинам находится под вопросом.
Самая легкая программа для 3D моделирования!
WaitForMultipleObjects ( dwCount , lpHandles , bWaitAll , dwTimeout ) ;
Процедура WaitForMultipleObjects работает аналогично WaitForSingleObject, но для массива объектов. Первый аргумент задает размер массива, должен быть строго больше нуля и не превышать MAXIMUM_WAIT_OBJECTS (который равняется в точности 64). Вторым аргументом задается указатель на массив хэндлов.
В массиве могут содержаться хэндлы на объекты разных типов, но многократное включение одного и того же хэндла считается ошибкой. Если третий аргумент равен TRUE, процедура ждет перехода в сигнальное состояние всех объектов, иначе — любого из указанных объектов. Семантика последнего аргумента точно такая же, как и в случае с WaitForSingleObject.
Процедура возвращает WAIT_FAILED в случае ошибки и WAIT_TIMEOUT в случае отваливания по таймауту. Если процедура вернула значение от WAIT_OBJECT_0 до WAIT_OBJECT_0 + dwCount — 1, то в случае bWaitAll == TRUE все объекты из массива перешли в сигнальное состояние, а в случае bWaitAll == FALSE в сигнальное состояние перешел объект с индексом код возврата минус WAIT_OBJECT_0. Если процедура вернула значение от WAIT_ABANDONED_0 до WAIT_ABANDONED_0 + dwCount — 1, то в случае bWaitAll == TRUE все ожидаемые объекты перешли в сигнальное состояние и по крайней мере один из объектов оказался брошенным мьютексом, а в случае bWaitAll == FALSE брошенным оказался мьютекс с индексом код возврата минус WAIT_ABANDONED_0.
Если bWaitAll == TRUE, процедура не меняет состояния объектов до тех пор, пока все они не перейдут в сигнальное состояние. Таким образом, пока вы ждете, мьютексы могут продолжать лочиться и анлочиться другими потоками. Если bWaitAll == FALSE и сразу несколько объектов перешли в сигнальное состояние, процедура всегда работает с объектом в массиве, имеющим минимальный индекс.
ReleaseMutex ( hMutex ) ;
ReleaseMutex анлочит ранее залоченный мьютекс. В случае успеха процедура возвращает значение, отличное от нуля. В случае ошибки возвращается ноль, а подробности доступны через GetLastError. Что характерно, для предотвращения дэдлоков Windows позволяет потокам лочить один и тот же мьютекс несколько раз, но и количество вызовов ReleaseMutex должно быть соответствующим.
Sleep ( dwMilliseconds ) ;
Процедура Sleep блокирует текущий поток на заданное количество миллисекунд. Если в качестве параметра передать INFINITE, поток будет заблокирован навсегда. Если передать ноль, поток уступает оставшуюся часть своей доли процессорного времени любой другой нити с таким же приоритетом. У процедуры нет возвращаемого значения (VOID).
ExitThread ( dwCode ) ;
ExitThread завершает текущий поток с заданным кодом возврата. Объект потока при этом переходит в сигнальное состояние. Если это был последний поток в текущем процессе, процесс завершается. Код возврата конкретного потока может быть получен с помощью GetExitCodeThread.
А теперь посмотрим на все это хозяйство в действии:
#define THREADS_NUMBER 10
#define ITERATIONS_NUMBER 100
#define PAUSE 10 /* ms */
DWORD dwCounter = 0 ;
DWORD WINAPI ThreadProc ( CONST LPVOID lpParam ) {
CONST HANDLE hMutex = ( CONST HANDLE ) lpParam ;
DWORD i ;
for ( i = 0 ; i < ITERATIONS_NUMBER ; i ++ ) {
WaitForSingleObject ( hMutex , INFINITE ) ;
dwCounter ++;
ReleaseMutex ( hMutex ) ;
Sleep ( PAUSE ) ;
}
ExitThread ( 0 ) ;
}
VOID Error ( CONST HANDLE hStdOut , CONST LPCWSTR szMessage ) {
DWORD dwTemp ;
TCHAR szError [ 256 ] ;
WriteConsole ( hStdOut , szMessage , lstrlen ( szMessage ) ,
wsprintf ( szError , TEXT ( «LastError = %d r n » ) , GetLastError ( ) ) ;
WriteConsole ( hStdOut , szError , lstrlen ( szError ) ,
ExitProcess ( 0 ) ;
}
INT main ( ) {
TCHAR szMessage [ 256 ] ;
DWORD dwTemp , i ;
HANDLE hThreads [ THREADS_NUMBER ] ;
CONST HANDLE hStdOut = GetStdHandle ( STD_OUTPUT_HANDLE ) ;
CONST HANDLE hMutex = CreateMutex ( NULL , FALSE , NULL ) ;
if ( NULL == hMutex ) {
Error ( hStdOut , TEXT ( «Failed to create mutex. r n » ) ) ;
}
for ( i = 0 ; i < THREADS_NUMBER ; i ++ ) {
hThreads [ i ] = CreateThread ( NULL , 0 ,
if ( NULL == hThreads [ i ] ) {
Error ( hStdOut , TEXT ( «Failed to create thread. r n » ) ) ;
}
}
WaitForMultipleObjects ( THREADS_NUMBER , hThreads , TRUE , INFINITE ) ;
wsprintf ( szMessage , TEXT ( «Counter = %d r n » ) , dwCounter ) ;
WriteConsole ( hStdOut , szMessage , lstrlen ( szMessage ) ,
for ( i = 0 ; i < THREADS_NUMBER ; i ++ ) {
CloseHandle ( hThreads [ i ] ) ;
}
CloseHandle ( hMutex ) ;
ExitProcess ( 0 ) ;
}
Здесь мы создаем десять потоков, каждый из которых увеличивает глобальный счетчик на единицу сто раз. Чтобы в счетчике не оказалась записана фигня, доступ к нему ограничивается с помощью мьютекса. Перед вызовом ExitProcess закрываются хэндлы мьютекса и всех созданных потоков. Строго говоря, в данном конкретном случае это не требуется, но в более общем случае все хэндлы за собой, разумеется, нужно вовремя закрывать. Также вы могли обратить внимание, что я прислушался к совету, который мне дали в комментариях к предыдущей заметке, и стал оборачивать строки в макрос TEXT.
Как обычно, программа может быть скомпилирована как в Visual Studio и запущена под Windows, так и с помощью MinGW и запущена под Wine. Ну и по традиции напоминаю, что ничто не делает блогера более грустным котиком, чем отсутствие каких-либо комментариев к его постам 🙂
Дополнение: Кстати, при помощи именованных мьютексов можно сделать так, чтобы пользователь мог запустить только один экземпляр приложения. Здесь можно посмотреть пример как это делается.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.
Источник: eax.me
С++ для новичков — что это за язык программирования, как писать программы: 1-я часть гайда
Разбираемся, как устроен язык программирования C++, что такое программы и как научиться их писать.
Евгений Кучерявый
Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.
Чтобы научиться программировать с нуля, нужно только уметь пользоваться компьютером, устанавливать программы, создавать папки и файлы. Это первая статья из серии «Глубокое погружение в C++», с помощью которой вы сделаете первые шаги в профессии разработчика серверных приложений и игр.
Не пугайтесь большого объёма информации: чем больше вы будете знать, тем лучше сможете программировать. Это особенно важно, если вы выбрали C++, потому что на этом языке невозможно без хорошей теоретической базы написать высокопроизводительный код.
Задачи и решение. С++
На этой странице собраны все задачи по темам уроков, размещенных на нашем сайте. К каждой задаче прилагается наш вариант решения. Постарайтесь всё же решать их самостоятельно и только потом сравнивать наш исходный код со своим. “Хочешь научиться программировать – программируй”!
Больше практических заданий с решениями найдёте здесь.
Рекомендуем посетить Сайт для юных программистов – где вы найдете уроки по различным языкам программирования (в том числе для детей), 3D-моделированию, Linux и др.
120 thoughts on “ Задачи и решение. С++ ”
Сайт бомба , но друзья переведите задачи на родной язык потому что не очень понятно о чем дело
А какой язык родной ?
здравствуйте
Султан :
#include using namespace std; class Date <
public:
int day, month, year;
Date () <>
Date(int day, int month, int year) <
this->day = day;
this->month = month;
this->year = year;
>
string toString() <
string d = “”, m = “”;
if (day < 10) d = «0»;
if (month car_model = car_model;
this->manufacturer = manufacturer;
this->class1 = class1;
this->year = year;
this->date = date;
>
Print() <
cout >
>; int main() < Car* Cars[] <
new Car(«AE86 Sprinter Trueno», «Toyota», «tSport car», 2001, Date(01, 01, 2006)),
new Car(«Skyline Xt», «Nissant», «Sport car», 2003, Date(12, 04, 2005)),
new Car(«Corvette C3 Cabriolet», «Chevrolet», «Sport car», 2000, Date(17, 05, 2002)),
new Car(«Ferrari F40t», «Ferrarit», «Sport car», 1969, Date(01, 06, 2017)),
new Car(«Celicatt», «Toyota», «tSport car», 1999, Date(03, 03, 2004))
>;
for (int i = 0; i manufacturer == “Toyota” Cars[i]->date.year Print();
>
return 0;
>
Створіть додаток для обчислення арифметичного виразу користувача. Користувач вводить з клавіатури деякий арифметичний вираз. Вираз може містити: (), +, -, *, /.
Додаток обчислює результат виразу з урахуванням дужок, пріоритетів. Результат виводиться на екран. Наприклад, якщо користувач ввів:
5*2 + 1
Результат: 11
Якщо користувач ввів:
5 * (2+1)
Помогите плс, срочно нужно, только без лишних библиотек и тд.
Создайте приложение для вычисления арифметического выражения пользователя. Пользователь вводит с клавиатуры некоторое арифметическое выражение. Выражение может содержать: (), +, -, *, /.
Приложение вычисляет результат выражения с учетом скобок, приоритетов. Результат выводится на экран. К примеру, если пользователь ввел:
5*2 + 1
Результат: 11
Если пользователь ввел:
5* (2+1)
Источник: purecodecpp.com