Сон в C#
В этом посте будет обсуждаться, как временно приостановить выполнение программы C# на указанное время.
Есть два механизма сна в C#:
1. Использование Thread.Sleep() метод ( System.Threading )
The Thread.Sleep() Метод приостанавливает текущий поток на указанное время. Для сна требуется общее количество миллисекунд. Обратите внимание, что Thread.Sleep полностью заблокирует поток и не будет потреблять мощность процессора.
В следующем примере метод Sleep используется для полной блокировки основного потока программы на 5 секунд.
7.11 – Остановки (преждевременный выход из программы)
Последняя категория инструкций управления порядком выполнения программы, которую мы рассмотрим, – это остановки. Остановка – это инструкция управления порядком выполнения программы, которая завершает программу. В C++ остановки реализованы как функции (а не как ключевые слова), поэтому наши инструкции остановки будут вызовами функций.
Давайте сделаем небольшой экскурс и вспомним, что происходит, когда программа завершается нормально. Когда функция main() завершается (либо достигая конца тела функции, либо с помощью инструкции return ), происходит ряд разных вещей.
Во-первых, поскольку мы выходим из функции, все локальные переменные и параметры функции уничтожаются (как обычно).
Затем вызывается специальная функция std::exit() со значением, возвращаемым из main() (код состояния), переданным в качестве аргумента. Так что же такое std::exit() ?
Функция std::exit()
std::exit() – это функция, которая вызывает нормальное завершение программы. Нормальное завершение означает, что программа завершилась ожидаемым образом. Обратите внимание, что термин «нормальное завершение» ничего не говорит о том, была ли работа программы успешной (для этого нужен код состояния). Например, предположим, что вы пишете программу, в которой ожидаете, что пользователь введет имя файла для обработки. Если пользователь ввел недопустимое имя файла, ваша программа, вероятно, вернет ненулевой код состояния, чтобы указать на состояние сбоя, но это всё равно будет нормальное завершение.
std::exit() выполняет ряд функций очистки. Сначала уничтожаются объекты со статической продолжительностью хранения. Затем выполняется дополнительная очистка файлов, если использовались какие-либо файлы. Наконец, управление возвращается обратно в ОС, а аргумент, переданный в std::exit() , используется в качестве кода состояния.
Явный вызов std::exit()
Хотя std::exit() при завершении функции main() вызывается неявно, она также может быть вызвана явно, чтобы остановить программу до того момента, как она завершится нормально. Когда std::exit() вызывается таким образом, вам нужно будет включить заголовок cstdlib .
Вот пример явного использования std::exit() :
Эта программа печатает:
Обратите внимание, что инструкции после вызова std::exit() никогда не выполняются, потому что программа уже завершена.
Хотя в приведенной выше программе мы вызываем std::exit() из функции main() , std::exit() можно вызвать из любой функции для завершения программы в необходимой точке.
Одно важное замечание о явном вызове std::exit() : std::exit() не очищает никакие локальные переменные (ни в текущей функции, ни в функциях вверх по стеку вызовов). По этой причине обычно лучше избегать вызова std::exit() .
Предупреждение
Функция std::exit() не очищает локальные переменные в текущей функции и в функциях выше по стеку вызовов.
std::atexit
Поскольку std::exit() завершает программу немедленно, вы можете перед завершением выполнить какую-либо очистку вручную (в приведенном выше примере мы вручную вызывали функцию cleanup() ). Поэтому программисту необходимо не забывать вручную вызывать функцию очистки перед каждым вызовом exit() .
Чтобы помочь в этом, C++ предлагает функцию std::atexit() , которая позволяет вам указать функцию, которая будет автоматически вызываться при завершении программы через std::exit() .
Эта программа имеет тот же вывод, что и предыдущий пример:
Так зачем вам это делать? Это позволяет вам указать функцию очистки в одном месте (возможно, в main ), а затем не беспокоиться о том, чтобы не забыть вызвать эту функцию явно перед вызовом std::exit() .
Несколько замечаний о std::atexit() и функции очистки: во-первых, поскольку std::exit() вызывается неявно при завершении main() , если программа завершается таким образом, это вызовет любые функции, зарегистрированные std::atexit() . Во-вторых, регистрируемая функция не должна принимать никаких параметров и должна не иметь возвращаемого значения. Наконец, с помощью std::atexit() вы можете зарегистрировать несколько функций очистки, если хотите, и они будут вызываться в порядке, обратном порядку регистрации (последняя зарегистрированная будет вызываться первой).
Для продвинутых читателей
В многопоточных программах вызов std::exit() может привести к сбою вашей программы (поскольку поток, вызывающий std::exit() , будет очищать статические объекты, к которым могут обращаться другие потоки). По этой причине в C++ появилась еще одна пара функций, которые работают аналогично std::exit() и std::atexit() , – std::quick_exit() и std::at_quick_exit() . std::quick_exit() завершает программу нормально, но не очищает статические объекты и может выполнять или не выполнять другие типы очистки. std::at_quick_exit() выполняет ту же роль, что и std::atexit() для программ, завершаемых с помощью std::quick_exit() .
std::abort и std::terminate
C++ также содержит две другие функции, связанные с остановкой.
Функция std::abort() вызывает аварийное завершение вашей программы. Аварийное завершение означает, что в программе произошла какая-то необычная ошибка времени выполнения, и программа не может продолжать работу. Например, к аварийному завершению попытка разделить на 0 приведет. std::abort() не выполняет никакой очистки.
Случаи, когда std::abort вызывается неявно, мы увидим в этой главе позже (7.17 – assert и static_assert ).
Функция std::terminate() обычно используется вместе с исключениями (мы рассмотрим исключения в следующей главе). Хотя std::terminate можно вызвать явно, она чаще вызывается неявно, когда исключение не обрабатывается (и в некоторых других случаях, связанных с исключениями). По умолчанию std::terminate() вызывает std::abort() .
Когда следует использовать остановки?
Короткий ответ: «почти никогда». В C++ уничтожение локальных объектов является важной частью C++ (особенно когда мы дойдем до классов), и ни одна из вышеупомянутых функций не очищает локальные переменные. Исключения – лучший и безопасный механизм обработки ошибок.
Лучшая практика
Используйте остановки только в том случае, если нет безопасного способа нормально вернуться из функции main . Если вы не отключили исключения, используйте их для безопасной обработки ошибок.
How do I make a C++ console program exit?
Is there a line of code that will terminate the program?
Something like python’s sys.exit() ?
13 Answers 13
While you can call exit() (and may need to do so if your application encounters some fatal error), the cleanest way to exit a program is to return from main() :
When you call exit() , objects with automatic storage duration (local variables) are not destroyed before the program terminates, so you don’t get proper cleanup. Those objects might need to clean up any resources they own, persist any pending state changes, terminate any running threads, or perform other actions in order for the program to terminate cleanly.
![]()
There are several ways to cause your program to terminate. Which one is appropriate depends on why you want your program to terminate. The vast majority of the time it should be by executing a return statement in your main function. As in the following.
As others have identified this allows all your stack variables to be properly destructed so as to clean up properly. This is very important.
If you have detected an error somewhere deep in your code and you need to exit out you should throw an exception to return to the main function. As in the following.
This causes the stack to be unwound an all your stack variables to be destructed. Still very important. Note that it is appropriate to indicate failure with a non-zero return value.
If in the unlikely case that your program detects a condition that indicates it is no longer safe to execute any more statements then you should use std::abort(). This will bring your program to a sudden stop with no further processing. std::exit() is similar but may call atexit handlers which could be bad if your program is sufficiently borked.
Yes! exit() . It’s in <cstdlib> .
Allowing the execution flow to leave main by returning a value or allowing execution to reach the end of the function is the way a program should terminate except under unrecoverable circumstances. Returning a value is optional in C++, but I typically prefer to return EXIT_SUCCESS found in cstdlib (a platform-specific value that indicates the program executed successfully).
If, however, your program reaches an unrecoverable state, it should throw an exception. It’s important to realise the implications of doing so, however. There are no widely-accepted best practices for deciding what should or should not be an exception, but there are some general rules you need to be aware of.
For example, throwing an exception from a destructor is nearly always a terrible idea because the object being destroyed might have been destroyed because an exception had already been thrown. If a second exception is thrown, terminate is called and your program will halt without any further clean-up having been performed. You can use uncaught_exception to determine if it’s safe, but it’s generally better practice to never allow exceptions to leave a destructor.
While it’s generally always possible for functions you call but didn’t write to throw exceptions (for example, new will throw std::bad_alloc if it can’t allocate enough memory), it’s often difficult for beginner programmers to keep track of or even know about all of the special rules surrounding exceptions in C++. For this reason, I recommend only using them in situations where there’s no sensible way for your program to continue execution.
exit is a hold-over from C and may result in objects with automatic storage to not be cleaned up properly. abort and terminate effectively causes the program to commit suicide and definitely won’t clean up resources.
Whatever you do, don’t use exceptions, exit , or abort / terminate as a crutch to get around writing a properly structured program. Save them for exceptional situations.
Как остановить программу в с
Параллельное выполнение задач может занимать много времени. И иногда может возникнуть необходимость прервать выполняемую задачу. Для этого платформа .NET предоставляет структуру CancellationToken из пространства имен System.Threading .
Общий алгоритм отмены задачи обычно предусматривает следующий порядок действий:
Создание объекта CancellationTokenSource , который управляет и посылает уведомление об отмене токену.
С помощью свойства CancellationTokenSource.Token получаем собственно токен — объект структуры CancellationToken и передаем его в задачу, которая может быть отменена.
Для передачи токена в задачу можно применять один из конструкторов класса Task:
Определяем в задаче действия на случай ее отмены.
Вызываем метод CancellationTokenSource.Cancel() , который устанавливает для свойства CancellationToken.IsCancellationRequested значение true. Стоит понимать, что сам по себе метод CancellationTokenSource.Cancel() не отменяет задачу, он лишь посылает уведомление об отмене через установку свойства CancellationToken.IsCancellationRequested . Каким образом будет происходить выход из задачи, это решает сам разработчик.
Класс CancellationTokenSource реализует интерфейс IDisposable. И когда работа с объектом CancellationTokenSource завершена, у него следует вызвать метод Dispose для освобождения всех связанных с ним используемых ресурсов. (Вместо явного вызова метода Dispose можно использовать конструкцию using).
Теперь касательно третьего пункта — определения действий отмены задачи. Как именно завершить задачу? Конкретные действия на лежат целиком на разработчике, тем не менее есть два общих варианта выхода:
При получении сигнала отмены выйти из метода задачи, например, с помощью оператора return или построив логику метода соответствующим образом. Но следует учитывать, что в этом случае задача перейдет в состояние TaskStatus.RanToCompletion , а не в состояние TaskStatus.Canceled .
При получении сигнала отмены сгенерировать исключение OperationCanceledException , вызвав у токена метод ThrowIfCancellationRequested() . После этого задача перейдет в состояние TaskStatus.Canceled .
Мягкий выход из задачи без исключения OperationCanceledException
Сначала рассмотрим первый — «мягкий» вариант завершения:
В данном случае задача task вычисляет и выводит на консоль квадраты чисел от 1 до 9. Для отмены задачи нам надо создать и использовать токен. Вначале создается объект CancellationTokenSource :
Затем из него получаем сам токен:
Чтобы отменить операцию, необходимо вызвать метод Cancel() у объекта CancellationTokenSource:
В данном случае отмена задачи вызывается через секунду, чтобы задача произвела некоторые действия.
В самом методе задачи в цикле мы можем отловить сигнал отмены с помощью проверки свойства token.IsCancellationRequested :
Если был вызван метод cancelTokenSource.Cancel() , то выражение token.IsCancellationRequested возвращает true.
После завершения задачи проверяем ее статус:
Поскольку задача успешно завершена, у задачи должен быть статус RanToCompletion
И в конце у объекта CancellationTokenSource вызываем метод Dispose:
Консольный вывод программы:
Отмена задачи с помощью генерации исключения
Второй способ завершения задачи представляет генерация исключения OperationCanceledException . Для этого применяется метод ThrowIfCancellationRequested() объекта CancellationToken:
Здесь опять же проверяем значение свойства IsCancellationRequested, и если оно равно true, генерируем исключение:
Чтобы обработать исключение, помещаем весь код работы с задачей в конструкцию try..catch и также с помощью вызова cancelTokenSource.Cancel() посылаем сообщение об отмене задачи.
Стоит отметить, что генерируемое исключение будет спрятано в объекте AggregateException, который по сути представляет набор исключений. Если причина исключения состояла в отмене задачи, то мы можем найти в этом наборе исключений исключение типа TaskCanceledException
Класс TaskCanceledException является производным от OperationCanceledException. Исключение типа TaskCanceledException возникает, если для задачи устанавливается статус Canceled .
Консольный вывод программы:
Стоит отметить, что исключение возникает только тогда, когда мы останавливаем текущий поток и ожидаем завершения задачи с помощью методов Wait или WaitAll. Если эти методы не используются для ожидания задачи, то для нее просто устанавливается состояние Canceled. Например, в следующем случае исключение не возникнет:
Консольный вывод программы:
Регистрация обработчика отмены задачи
Выше для проверки сигнала отмены применялось свойство IsCancellationRequested. Но есть и другой способ узнать о том, что был послан сигнал отмены задачи. Метод Register() позволяет зарегистрировать обработчик отмены задачи в виде делегата Action:
Здесь обработчик отмены представлен лямбда-выражением:
Поскольку действие задачи представляет цикл, который выполняется при значении i меньше 10, то установка этой переменной в обработчике отмены приведет к выходу из цикла и соответственно завершению задачи.
Передача токена во внешний метод
Если операция, которая выполняется в задаче, представляет внешний метод, то ему можно передавать в качестве одного из параметров: