#c #multithreading #compiler-errors #c 14 #future
#c #многопоточность #ошибки компилятора #c 14 #будущее
Вопрос:
У меня есть класс с именем cApp
.
Я хочу работать CheckProcessList()
в фоновом режиме, пока программа не завершится. Итак, я подумал, ну, давайте запустим его в отдельном потоке, пока ~cApp()
. Я создал bool для выхода из цикла CheckProcessList()
. В ~cApp
я устанавливаю значение bool true m_bTerminateThread = true
для прерывания и жду обещания m_barrierFuture->wait()
, что поток завершил выполнение. После прерывания я установил обещание barrier.set_value()
, что поток теперь завершает выполнение. Теперь ~cApp
можно завершить выполнение. Или, по крайней мере, это мое понимание того, чего я хочу достичь и как это сделать. Ну, не может быть, так как я получаю ошибки компилятора.
Почему он хотел проверить, закончился ли поток в первую очередь? Потому что программа прерывается во время выполнения, когда она завершается, и поток в этот момент GetProcId()
включен . Если он находится в спящем режиме в момент завершения, программа не прерывается.
Я искал ответы в msdn и stackoverflow, но я не получаю из этого ничего, что я могу понять. Я использую VS2019 и C 14. Заранее благодарю вас, ребята.
cApp.h
#pragma once
#include "wx/wx.h"
#include "cMain.h"
#include <thread>
#include <future>
class cApp
: public wxApp
{
public:
cApp();
~cApp();
virtual bool OnInit();
private:
// supposed to run in a detached thread
// until the program terminates
void CheckProcessList(std::promise<void> barrier);
// Checks whether or not the game processes are running
// this thread runs asynchronous until ~cApp
std::thread* m_tCheckProcList;
// used in thread "m_tCheckProcList"
// if set to true the thread terminates asap
bool m_bTerminateThread;
// used in thread "m_tCheckProcList"
// in ~cApp this future waits for the promise that the thread has finished
std::future<void>* m_barrierFuture;
// Dark Souls 3 Processname
const wchar_t* m_ds3Name;
// Need for Speed: Most Wanted Processname
const wchar_t* m_nfsmwName;
// Serious Sam: The Second Encounter Processname
const wchar_t* m_sstseName;
const wxString* m_frameTitle;
const wxSize* m_frameSize;
cMain* m_mainFrame;
};
cApp.cpp
#include "cApp.h"
wxIMPLEMENT_APP(cApp);
cApp::cApp()
{
m_ds3Name = L"DarkSoulsIII.exe";
m_sstseName = L"SeriousSam.exe";
m_nfsmwName = L"speed.exe";
m_frameTitle = new wxString("DeltaWin");
m_frameSize = new wxSize(600, 450);
m_bTerminateThread = false;
m_mainFrame = nullptr;
m_tCheckProcList = nullptr;
m_barrierFuture = nullptr;
}
cApp::~cApp()
{
// send the thread the "signal" to finish asap
m_bTerminateThread = true;
// wait for thread "m_tCheckProcList" to finish execution
m_barrierFuture->wait();
}
bool cApp::OnInit()
{
// create main top-level window
m_mainFrame = new cMain(*m_frameTitle, wxDefaultPosition, *m_frameSize);
m_mainFrame->Show();
// create barrier and instantiate the future for it
std::promise<void> barrier;
m_barrierFuture = new std::future<void>(barrier.get_future());
// start checking for running game processes in asynchronous thread
m_tCheckProcList = new std::thread(amp;cApp::CheckProcessList, std::move(barrier));
m_tCheckProcList->detach();
return true;
}
void cApp::CheckProcessList(std::promise<void> barrier)
{
while (!m_bTerminateThread)
{
// Dark Souls 3
if (GetProcId(m_ds3Name) == 0)
m_mainFrame->MenuItemEnable(false, menuItem::DarkSouls3);
else
m_mainFrame->MenuItemEnable(true, menuItem::DarkSouls3);
// Need for Speed: Most Wanted
if (GetProcId(m_nfsmwName) == 0)
m_mainFrame->MenuItemEnable(false, menuItem::NFSMostWanted);
else
m_mainFrame->MenuItemEnable(true, menuItem::NFSMostWanted);
// Serious Sam: The Second Encounter
if (GetProcId(m_sstseName) == 0)
m_mainFrame->MenuItemEnable(false, menuItem::SeriousSamTSE);
else
m_mainFrame->MenuItemEnable(true, menuItem::SeriousSamTSE);
// Sleep 1.5s to save resources
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
}
// set the promise that the thread has ended execution
barrier.set_value();
}
Редактировать:
После этого программа завершается ~cApp
. Поэтому я думаю, что в данном конкретном случае мне не нужна delete
вся эта память, потому что об этом заботится ОС.
правка2:
C2893: не удалось специализировать шаблон функции «std неизвестного типа::invoke(_Callable amp;amp;,_Ty1 amp;amp;,_Types2 amp;amp;…) noexcept()».
C2780: «std неизвестного типа::invoke(_Callable amp;amp;) noexcept()»: ожидается 1 аргумент — предоставлено 2
C2672: «вызов»: не найдена соответствующая перегруженная функция
Комментарии:
1. У вас куча утечек памяти. Вы должны
delete
выделить память.2. @bloody я подумал, что в этом случае нет необходимости
delete
в памяти, потому что она все равно освобождается ОС после завершения программы. Вот почему я неdelete
все это делаю~cApp
. Я ошибаюсь в этом?3. Первый — на-один может знать это, вы просто показали класс, а не контекст его использования (возможно, вы создаете сотни таких объектов). Во-вторых, даже если да, всегда освобождайте память! Это единственная хорошая школа программирования и привыкания к правильному запоминанию внимания. Или используйте интеллектуальные указатели вместо
new
, чтобы избежать этого бремени.4. тогда, черт возьми, я виноват. Я добавил «Программа завершается после
~cApp
» к сообщению в надежде, что это прояснит ситуацию. Спасибо за ваш ответ!5. Вы должны указывать сообщения об ошибках в вопросе, а не только их номера. Читатели могут быть не очень склонны изучать в Интернете, что на самом деле означают эти номера ошибок.
Ответ №1:
m_tCheckProcList = new std::thread(amp;cApp::CheckProcessList, std::move(barrier));
Я не знаю, к каким строкам относятся ваши ошибки (вы этого не показали), но я подозреваю, что по крайней мере приведенное выше утверждение неверно.
Если вы передаете адрес процедуры потока в std::thread
constructor, и эта процедура является нестатической функцией-членом, следующим аргументом после должен быть адрес объекта, на который вы ссылаетесь (в конце концов, нестатическая функция-член должна иметь экземпляр, от имени которого она вызывается). std::promise
это не тип, который содержит такой тип указателя на функцию amp;cApp::CheckProcessList
, поэтому это не может работать.
Если вы хотите связать этот поток с объектом, который его создает, обычно такой вызов выглядит следующим образом:
std::thread(amp;cApp::CheckProcessList, this, ...
Или можно использовать либо статическую функцию-член, либо свободную функцию.
cApp::CheckProcessList(std::promise<void> barrier)
Еще одна проблема в вашем коде — передача объекта promise по значению в функцию thread . Это означает локальную копию promise, но promise невозможно скопировать. Вы также не можете передать его ни по ссылке, ни по указателю! because barrier
является локальной переменной OnInit()
метода, и как только этот метод завершается, эта переменная все равно уничтожается — базовый отдельный поток будет работать с недопустимым стековым фреймом или вызывать неопределенное поведение любого рода. Возможно, вы могли бы сделать элемент barrier
данных a или пересмотреть свой дизайн.
Будьте очень осторожны при работе с отсоединенными потоками. При неправильном использовании они полны подводных камней.
Комментарии:
1. Я выбрал это
std::thread(amp;cApp::CheckProcessList, this, ...
решение, потому что, если я отмечуcApp::CheckProcessList(std::promise<void> barrier)
как статический, я должен пометить все переменные-члены, используемые в этой функции, как статические, и я не думаю, что хочу этого. Я, вероятно, полностью переосмыслил дизайн, как вы сказали. (с использованием интеллектуальных указателей и т. Д.) Но именно поэтому я использовалstd::move(barrier)
, чтобы не делать его членом данных, поскольку apromise
следует использовать только один раз. Вот почему я решил объявить вOnInit()
и толькоstd::move()
это. Мне нужно многому научиться. Спасибо за вашу помощь!