Ошибки C C2893, C2780, C2672 при использовании будущего, обещания, отдельного потока

#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) , чтобы не делать его членом данных, поскольку a promise следует использовать только один раз. Вот почему я решил объявить в OnInit() и только std::move() это. Мне нужно многому научиться. Спасибо за вашу помощь!