Многопоточность: завершение потоков в MFC
Две обычные ситуации вызывают завершение потока: управление функцией завершается или поток не может выполняться до завершения. Если обработчик слов использовал поток для фоновой печати, управляющая функция завершается нормально, если печать выполнена успешно. Если пользователь хочет отменить печать, однако поток фоновой печати должен быть завершен преждевременно. В этом разделе объясняется, как реализовать каждую ситуацию и как получить код выхода потока после его завершения.
- Обычное завершение потока
- Преждевременное завершение потока
- Получение кода выхода потока
Обычное завершение потока
Для рабочего потока обычное завершение потока просто: выйти из управляемой функции и вернуть значение, указывающее причину завершения. Можно использовать функцию AfxEndThread или инструкцию return . Как правило, 0 означает успешное завершение, но это до вас.
Для потока пользовательского интерфейса процесс так же прост: из потока пользовательского интерфейса вызовите PostQuitMessage в пакете SDK для Windows. Единственным параметром, который PostQuitMessage принимает, является код выхода потока. Что касается рабочих потоков, 0 обычно означает успешное завершение.
Преждевременное завершение потока
Завершение потока преждевременно почти так просто: вызов AfxEndThread из потока. Передайте нужный код выхода в качестве единственного параметра. Это останавливает выполнение потока, освобождает стек потока, отсоединяет все библиотеки DLL, подключенные к потоку, и удаляет объект потока из памяти.
AfxEndThread необходимо вызвать из потока, чтобы завершить работу. Если вы хотите завершить поток из другого потока, необходимо настроить метод связи между двумя потоками.
Получение кода выхода потока
Чтобы получить код выхода рабочей роли или потока пользовательского интерфейса, вызовите функцию GetExitCodeThread . Сведения об этой функции см. в пакете SDK для Windows. Эта функция принимает дескриптор потока (хранящегося в m_hThread элементе CWinThread данных объектов) и адрес DWORD.
Если поток по-прежнему активен, GetExitCodeThread помещает STILL_ACTIVE в указанный адрес DWORD; в противном случае код выхода помещается в этот адрес.
Получение кода выхода объектов CWinThread занимает дополнительный шаг. По умолчанию при CWinThread завершении потока объект потока удаляется. Это означает, что невозможно получить доступ к элементу m_hThread данных, так как CWinThread объект больше не существует. Чтобы избежать этой ситуации, выполните одно из следующих действий:
- Задайте для m_bAutoDelete элемента данных значение FALSE. Это позволяет CWinThread объекту выжить после завершения потока. После завершения потока можно получить доступ к элементу m_hThread данных. Однако если вы используете этот метод, вы несете ответственность за уничтожение CWinThread объекта, так как платформа не будет автоматически удалять ее для вас. Это является предпочтительным методом.
- Храните дескриптор потока отдельно. После создания потока скопируйте его m_hThread член данных (с помощью ::DuplicateHandle ) в другую переменную и получите к ней доступ. Таким образом, объект удаляется автоматически при завершении, и вы по-прежнему можете узнать, почему поток завершился. Будьте осторожны, что поток не завершается, прежде чем можно дублировать дескриптор. Самый безопасный способ сделать это — передать CREATE_SUSPENDED в AfxBeginThread, сохранить дескриптор, а затем возобновить поток путем вызова ResumeThread.
Любой CWinThread метод позволяет определить, почему объект завершился.
pthreads завершить поток
Привет. Помогите, плиз, с потоками, а то оно всё какое-то страшное. Надо выполнить гарантированное завершение потока, и при этом не завесить «управляющий» поток. То есть, пусть в потоке крутится какой-то цикл, и опрашивает переменную ThreadTerminated, при установке которой в 1 поток благополучно завершается сам. Тут вопросов нет. Но если вдруг этот поток где-то внутри завис, на такой случай не пойму как это проверить и завершить. Под виндами я делал примерно так:
if WaitForSingleObject(hThread, 5000) = WAIT_TIMEOUT then TerminateThread(hThread);
И всё. Если поток нормально работает, то он быстренько проверит значение ThreadTerminate и «умрет» самостоятельно. Если же он висит, то через 5 секунд будет прибит принудительно, но главное — поток в котором приведенный код выполняется, так или иначе, продолжит работу, а поток hThread будет, так или иначе, завершен.
Ближайший аналог в pthreads, который я нашел — это pthread_join(thread, value). Но тайм-аут в нем не предусмотрен, т.е. если поток положил большой болт на значение ThreadTerminate, то при выполнении pthread_join() повиснет и этот, вызвавший функцию, поток. А если join не делать, то есть вероятность, что тот поток так и продолжит висеть. Можно, конечно, вызвать pthread_cancel(), но для этого надо знать, что поток самостоятельно умирать не желает. А как это проверить без join?
Как принудительно завершить поток?
С помощью async запускаю в main поток, в котором крутится бесконечный цикл. Как завершить этот поток?
Отслеживать
Andrej Levkovitch
задан 26 апр 2018 в 11:25
Andrej Levkovitch Andrej Levkovitch
8,047 2 2 золотых знака 19 19 серебряных знаков 46 46 бронзовых знаков
добавить фалг выхода из бесконечного цикла.
26 апр 2018 в 11:28
Варианты: 1. добавить всётаки флаг. Обнулять извне. 2. Делать потоку terminate. 3. Завершить процесс через terminate — тогда завершится всё.
26 апр 2018 в 11:31
Скорее автору надо определится, что он хочет — иметь бесконечный цикл или иметь возможность завершать его. Это взаимоисключающие варианты.
26 апр 2018 в 11:35
Бесконечный цикл, с точки зрения языка С++, является неопределённым поведением. Поэтому добавляйте условие выхода и используйте его.
26 апр 2018 в 12:01
@AndrejLevkovitch ну тогда да, заведите флаг завершения потока, защитите доступ к нему с помощью, например, std::mutex , и заваршайте цикл по флагу. А снаружи после изменения флага делайте std::join
26 апр 2018 в 12:44
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
*.pro файл для QtCreator:
TEMPLATE = app CONFIG += console c++11 CONFIG -= app_bundle CONFIG -= qt LIBS += -lpthread SOURCES += main.cpp
#include #include #include #include #include #include using namespace std; bool ExitFlag = false; int someVariable = 0; std::mutex threadMutex; void threadFunc() < while (true)< std::this_thread::sleep_for(std::chrono::seconds(1)); someVariable = std::rand() % 100; std::cout > > int main()
var:83 var:86 var:77 var:15 var:93 var:35 var:86 var:92 var:49 var:21 Hello World!
Как закрыть поток c
Для работы с файлами в стандартной библиотеке определен заголовочный файл fstream , который определяет базовые типы для чтения и записи файлов. В частности, это:
- ifstream : для чтения с файла
- ofstream : для записи в файл
- fstream : совмещает запись и чтение
Для работы с данными типа wchar_t для этих потоков определены двойники:
- wifstream
- wofstream
- wfstream
Открытие файла
При операциях с файлом вначале необходимо открыть файл с помощью функции open() . Данная функция имеет две версии:
- open(путь)
- open(путь, режим)
Для открытия файла в функцию необходимо передать путь к файлу в виде строки. И также можно указать режим открытия. Список доступных режимов открытия файла:
- ios::in : файл открывается для ввода (чтения). Может быть установлен только для объекта ifstream или fstream
- ios::out : файл открывается для вывода (записи). При этом старые данные удаляются. Может быть установлен только для объекта ofstream или fstream
- ios::app : файл открывается для дозаписи. Старые данные не удаляются.
- ios::ate : после открытия файла перемещает указатель в конец файла
- ios::trunc : файл усекается при открытии. Может быть установлен, если также установлен режим out
- ios::binary : файл открывается в бинарном режиме
Если при открытии режим не указан, то по умолчанию для объектов ofstream применяется режим ios::out , а для объектов ifstream — режим ios::in . Для объектов fstream совмещаются режимы ios::out и ios::in .
std::ofstream out; // поток для записи out.open("hello1.txt"); // окрываем файл для записи std::ofstream out2; out2.open("hello2.txt", std::ios::app); // окрываем файл для дозаписи std::ofstream out3; out2.open("hello3.txt", std::ios::out | std::ios::trunc); // установка нескольких режимов std::ifstream in; // поток для чтения in.open("hello4.txt"); // окрываем файл для чтения std::fstream fs; // поток для чтения-записи fs.open("hello5.txt"); // окрываем файл для чтения-записи
Однако в принципе необязательно использовать функцию open для открытия файла. В качестве альтернативы можно также использовать конструктор объектов-потоков и передавать в них путь к файлу и режим открытия:
fstream(путь) fstream(путь, режим)
При вызове конструктора, в который передан путь к файлу, данный файл будет автоматически открываться:
std::ofstream out("hello.txt"); std::ifstream in("hello.txt"); std::fstream fs("hello.txt", std::ios::app);
В данном случае предполагается, что файл «hello.txt» располагается в той же папке, где и файл программы.
Вообще использование конструкторов для открытия потока является более предпочтительным, так как определение переменной, представляющей файловой поток, уже преполагает, что этот поток будет открыт для чтения или записи. А использование конструктора избавит от ситуации, когда мы забудем открыть поток, но при этом начнем его использовать.
В процессе работы мы можем проверить, окрыт ли файл с помощью функции is_open() . Если файл открыт, то она возвращает true:
std::ifstream in; // поток для чтения in.open(«hello.txt»); // окрываем файл для чтения // если файл открыт if (in.is_open())
Закрытие файла
После завершения работы с файлом его следует закрыть с помощью функции close() . Также стоит отметить, то при выходе объекта потока из области видимости, он удаляется, и у него автоматически вызывается функция close.
#include #include int main() < std::ofstream out; // поток для записи out.open("hello.txt"); // окрываем файл для записи out.close(); // закрываем файл std::ifstream in; // поток для чтения in.open("hello.txt"); // окрываем файл для чтения in.close(); // закрываем файл std::fstream fs; // поток для чтения-записи fs.open("hello.txt"); // окрываем файл для чтения-записи fs.close(); // закрываем файл >