Как я могу гарантировать быстрое завершение работы моего приложения win32? - PullRequest
7 голосов
/ 16 октября 2008

У меня есть приложение C32 Win32, в котором есть несколько потоков, которые могут быть заняты выполнением операций ввода-вывода (HTTP-вызовы и т. Д.), Когда пользователь хочет закрыть приложение. В настоящее время я играю хорошо и жду окончания всех потоков, прежде чем вернуться из main. Иногда это занимает больше времени, чем мне бы хотелось, и, действительно, кажется бессмысленным заставлять пользователя ждать, когда я смогу просто выйти. Однако, если я просто вернусь и вернусь с main, я, скорее всего, получу сбои, когда деструкторы начнут вызываться, пока еще есть потоки, использующие объекты.

Итак, признавая, что в идеальном платоническом мире добродетели лучше всего подождать, пока все потоки выйдут, а затем аккуратно завершить работу, каково будет следующее лучшее решение в реальном мире? Простое ускорение выхода из потоков может быть невозможным. Цель состоит в том, чтобы как можно быстрее остановить процесс, чтобы, например, поверх него можно было установить новую версию. Единственный дисковый ввод-вывод, который я делаю, находится в транзакционной базе данных, так что я не очень беспокоюсь о том, чтобы надеть на него пробку.

Ответы [ 11 ]

7 голосов
/ 16 октября 2008

Используйте перекрывающийся ввод-вывод, чтобы вы всегда контролировали потоки, связанные с вашим вводом-выводом, и всегда могли остановить их в любой момент; вы либо ожидаете их на IOCP и можете опубликовать в нем код завершения работы уровня приложения, либо вы можете ждать события в вашей структуре OVERLAPPED и ждать события «все потоки, пожалуйста, завершите сейчас».

В целом, избегайте блокирования вызовов, которые нельзя отменить.

Если вы не можете, и вы застряли в блокирующем вызове сокета, выполняя IO, тогда вы всегда можете просто закрыть сокет из потока, который решил, что пора завершать работу, и чтобы поток, который выполняет IO, всегда проверял Событие 'shutdown now' перед повторной попыткой ...

5 голосов
/ 17 октября 2008

Я использую технику на основе исключений, которая довольно хорошо работала для меня в ряде приложений Win32.

Чтобы завершить поток, я использую QueueUserAPC(), чтобы поставить в очередь вызов функции, которая выдает исключение. Однако выбрасываемое исключение не является производным от типа «Исключение», поэтому будет перехватываться только процедурой-оболочкой моего потока.

Преимущества этого заключаются в следующем:

  • Не требуется специального кода в вашем потоке, чтобы сделать его «остановимым» - как только он перейдет в состояние ожидания с оповещением, он запустит функцию APC.
  • Все деструкторы вызываются, поскольку исключение запускает стек, поэтому ваш поток завершается корректно.

Вещи, за которыми нужно следить:

  • Все, что делает catch (...), съест ваше исключение. Код пользователя всегда должен использовать catch(const Exception &e) или аналогичный!
  • Убедитесь, что ваш ввод / вывод и задержки выполняются «оповещаемым» способом. Например, это означает вызов sleepex(N, true) вместо sleep(N).
  • Связанные с ЦП потоки должны периодически вызывать sleepex(0,true) для проверки завершения.

Вы также можете «защитить» области своего кода, чтобы предотвратить завершение задачи во время критических разделов.

2 голосов
/ 16 октября 2008

Я бы порекомендовал использовать ваш графический интерфейс и работать над разными потоками. Когда пользователь запрашивает завершение работы, немедленно закрывайте графический интерфейс, создавая впечатление, что приложение закрыто. Разрешить рабочим потокам корректно закрываться в фоновом режиме.

2 голосов
/ 16 октября 2008

Лучший способ: выполняйте свою работу во время работы приложения и ничего не делайте (или как можно ближе к нему) при завершении работы (работает и при запуске). Если вы придерживаетесь этого паттерна, то можете сразу же разорвать потоки (вместо того, чтобы «быть вежливыми» по этому поводу), когда приходит запрос на отключение, не беспокоясь о работе, которую еще нужно сделать.

В вашей конкретной ситуации вам, вероятно, придется ждать завершения ввода-вывода (пишет, по крайней мере), если вы выполняете там локальную работу. HTTP-запросы и тому подобное, вы, вероятно, можете просто отказаться / закрыть сразу (опять же, если вы что-то не пишете). Но если это тот случай, когда вы пишете во время этого завершения и ждете этого, вы можете уведомить пользователя об этом, вместо того, чтобы ваш процесс выглядел зависшим во время завершения работы.

0 голосов
/ 09 декабря 2011

*NULL = 0 - самый быстрый способ. если вы не хотите аварийно завершить работу, позвоните по номеру exit() или его эквиваленту win32.

0 голосов
/ 17 октября 2008

Вы можете вызвать TerminateProcess - это немедленно остановит процесс, не уведомляя никого и ничего не ожидая.

0 голосов
/ 17 октября 2008

Если вам нужно внезапно завершить работу: просто вызовите ExitProcess, который будет вызываться, как только вы все равно вернетесь из WinMain. Сама Windows создает много рабочих потоков, которые не могут быть очищены - они прекращаются при завершении процесса.

Если у вас есть какие-либо потоки, которые выполняют записи какого-либо рода - очевидно, что им нужен шанс закрыть свои ресурсы. Но все остальное - игнорируйте предупреждения проверки границ и просто вытащите коврик из-под ног.

0 голосов
/ 17 октября 2008

Что бы вы ни делали, НЕ используйте TerminateThread, особенно для всего, что может быть в вызовах ОС HTTP. Вы можете потенциально сломать IE до перезагрузки.

Измените все свои операции ввода-вывода на асинхронную или неблокирующую модель, чтобы они могли отслеживать события завершения.

0 голосов
/ 17 октября 2008

Однажды у меня была похожая проблема, хотя и в Visual Basic 6: потоки из приложения могли подключаться к разным серверам, загружать некоторые данные, выполнять некоторые операции с этими циклами и сохранять результат на централизованном сервере.

Затем новым требованием было прекращение потоков из основной формы. Я выполнил это простым, хотя и грязным способом, остановив потоки после N циклов (эквивалентно примерно полсекунде), чтобы попытаться открыть мьютекс с конкретным именем. После успеха они немедленно прекратили все, что делали, и бросили, продолжили в противном случае.

Этот мьютекс был создан только основной формой, после того как все потоки вскоре закроются. Недостатком было то, что пользователю нужно было указать вручную, что он хочет снова запустить потоки - еще одна кнопка «Включить потоки для запуска» сделала это, освободив мьютекс: D

Этот трюк гарантированно работает, поскольку операции мьютекса являются атомарными. Проблема в том, что вы никогда не уверены, что поток действительно закрыт - сбой в логике обработки случая «openMutex преуспел» может означать, что он никогда не закончится. Вы также не знаете, когда / если все потоки закрылись (при условии, что ваш код прав, это займет примерно столько же времени, сколько потребуется циклам для остановки и «прослушивания»).

С моделью многопоточности VB "квартира" довольно сложно отправлять информацию из потоков в основное приложение туда-сюда, гораздо проще "запускать и забывать" или отправлять ее только из основного приложения в нить. Таким образом, необходимость такого рода сокращений. Используя C ++, вы можете свободно использовать свою многопоточную модель, поэтому эти ограничения могут на вас не распространяться.

0 голосов
/ 16 октября 2008

Если вы хотите беспрепятственно вытащить пробку, выход (0) сделает свое дело.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...