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

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

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


person twk    schedule 16.10.2008    source источник


Ответы (11)


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

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

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

person Len Holgate    schedule 16.10.2008

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

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

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

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

На что нужно обратить внимание:

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

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

person Roddy    schedule 16.10.2008

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

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

person TheSmurf    schedule 16.10.2008

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

person luke    schedule 16.10.2008
comment
Если вы имеете в виду установку главного окна в невидимый режим, то это не решит основную проблему, только косметическую. Кроме того, вопрос заключается в том, чтобы сказать потокам, чтобы они остановились СЕЙЧАС, чтобы не скрывать, что они все еще работают. - person Joe Pineda; 17.10.2008

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

person Paul Nathan    schedule 16.10.2008
comment
IIRC для многопоточных приложений вам нужно вызвать _exit (), а не exit (). Я искал документацию по этому поводу, но сейчас не могу ее найти. - person paxos1977; 16.10.2008

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

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

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

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

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

person Joe Pineda    schedule 16.10.2008

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

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

person user28709    schedule 16.10.2008

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

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

person Chris Becke    schedule 17.10.2008

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

person Nir    schedule 17.10.2008

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

person Dani    schedule 09.12.2011

Попросите пользователя отключить компьютер. Если не считать этого, вы должны отказаться от асинхронных действий на ветер. Или это HWIND? Я никогда не могу вспомнить на C ++. Конечно, вы можете пойти по середине пути и быстро отметить в текстовом файле или регистре, какое действие было отменено, чтобы при следующем запуске программы она могла автоматически выполнить это действие или спросить пользователя, хотят ли они это сделать. В зависимости от того, какие данные вы потеряете при отказе от асинхронного действия, вы не сможете этого сделать. Если вы взаимодействуете с пользователем, вы можете рассмотреть диалог или какое-либо взаимодействие с пользовательским интерфейсом, которое объясняет, почему это занимает так много времени.

Лично я предпочитаю инструкцию пользователю просто отключить компьютер от сети. :)

person Tyler Jensen    schedule 16.10.2008
comment
дайте смешной ответ, модифицируйтесь до бездны, похоже ... У людей нет чувства юмора ... - person tloach; 16.10.2008
comment
Да, мне больно. Может, их стек переполнился. - person Tyler Jensen; 17.10.2008
comment
DIGG или Slashdot уже далеко ----- › - person jim; 17.10.2008