多线程的C++ atexit处理程序与仅仅让操作系统从轨道上对其进行核处理

hiz5n14c  于 2023-05-30  发布在  其他
关注(0)|答案(1)|浏览(128)

我有一个主线程,它有一个控制台窗口。我使用std::atexit()来注册atexit_handler()。然后我使用SetConsoleCtrlHandler注册一个函数,该函数的全部目的是检查传递的DWORD是否等于CTRL_CLOSE_EVENT,如果是,则调用atexit_handler()。main函数有一个while循环,检查global::exit是否为true(global::exit是main. h中命名空间内的布尔值)。当while循环中断时,下一步是调用atexit_handler(),然后返回0。
问题是还有一个单独的线程,让我们称之为monitorThread,它可以在任何时候声明是时候关闭了,但是如果我通过设置global::exit为true来这样做,那么这是一个多线程(不只是这两个),必须与该共享变量交互。另一方面,我可以让monitorThread调用atexit_handler(),并让atexit_handler()在join()调用其他线程之前将exit设置为true,但是main最终会再次调用atexit_handler()。
所以,我的总体问题是,既然没有数据需要刷新到磁盘,我真的需要退出处理函数来释放内存和加入线程吗?或者我可以简单地让monitorThread调用exit(),让操作系统从轨道上清除一切?
如果我可以简单地让操作系统处理它,那么通过让我完全摆脱退出bool来简化事情。
以下是我目前拥有的:
GamewindowAndInput.h:

#pragma once
#include <Windows.h>
namespace GWAI {
    HWND getHWND();
    void inputLoop();
    void forkWaiter();
}

GamewindowAndInput.cpp:

#pragma once
#include "GamewindowAndInput.h"
#include <iostream>
#include "main.h"

HWND GWAI::getHWND() {
    HWND hwnd = FindWindow(NULL, L"[redacted]");
    if (hwnd == NULL) {
        "Game window not found, launching";
        //system("start [redacted]");
        //instead, fork exec ^, spawn new thread to wait on pid before setting exit to true
        Sleep(5000);
        hwnd = FindWindow(NULL, L"[redacted]");
    }
    while (hwnd == NULL) {
        std::cout << "Game window not found" << std::endl;
        Sleep(100);
        hwnd = FindWindow(NULL, L"[redacted]");
    }
    std::cout << "Valid HWND" << std::endl;
    return hwnd;
}
void GWAI::forkWaiter() {

}
void GWAI::inputLoop() {
    while (true) {
        //migrate from main.cpp
    }
}

main.h:

#pragma once
#include <Windows.h>
int main(int argc, char* argv[]);
namespace global {
    bool run = false, exit = false; //replace with atomic bool?
    HWND hwnd;
}
BOOL WINAPI cntrlHandler(DWORD type);
void atexit_handler();

main.cpp:

#pragma once
#include <Windows.h>
#include <iostream>
#include <cstdlib>
#include "main.h"
#include "GamewindowAndInput.h"
using std::cout; using std::endl;

constexpr int EVERY_NTH = 2;

int main(int argc, char* argv[]) {
    std::atexit(atexit_handler);
    SetConsoleCtrlHandler(cntrlHandler, TRUE);
    //create thread of &inputLoop

    global::hwnd = GWAI::getHWND();

    long i = 0;
    while (!global::exit) {
        if (global::hwnd != GetForegroundWindow()) {
            global::run = false;
            Sleep(10);
            continue;
        }
        if (GetKeyState(VK_NUMPAD3) & 0x8000/*Check if high-order bit is set (1 << 15)*/) {
            global::run = true;
            Sleep(500);
        }
        if (global::run) {
            while (global::hwnd == GetForegroundWindow()) {
                if (GetKeyState(VK_NUMPAD3) & 0x8000/*Check if high-order bit is set (1 << 15)*/) {
                    global::run = false;
                    Sleep(500);
                    break;
                }

                //insert actual main function here
                    
                if (global::exit)
                    goto begone;
            }
        }
    }
    begone:
    atexit_handler();
    return 0;
}

void atexit_handler(){
    //join threads
}
BOOL WINAPI cntrlHandler(DWORD type) {
    if (type == CTRL_CLOSE_EVENT) {
        atexit_handler();
        return TRUE;
    }
    return FALSE;
}
lhcgjxsq

lhcgjxsq1#

当使用线程时,良好的关闭与良好的启动和同步一样重要,以避免数据中断。
下面是我用代码写的一个例子。在线演示此处https://onlinegdb.com/6IkjrQWZN

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>

using namespace std::chrono_literals; // so we can use notations like 10ms, or 1s

class gwai_t final
{
public:
    gwai_t() = default;

    ~gwai_t()
    {
        stop();
        m_thread.join(); 
    }

    void start()
    {
        // use lambda expression : https://en.cppreference.com/w/cpp/language/lambda
        // to start the thread
        m_thread = std::thread{ [&] { loop(); } };

        // wait for the background thread to have started
        wait_for_state(state_v::running);
    }

    void stop()
    {
        // scopes matter this is one to control the lifetime of the lock
        {
            std::unique_lock<std::mutex> lock{ m_mtx };
            if (m_state != state_v::running) return;
            m_state = state_v::stopping;
            m_condition_variable.notify_all();
        }

        wait_for_state(state_v::stopped);
    }

private:
    // A class with threads should have a state
    // to communicate and synchronize with other threads
    enum class state_v
    {
        idle,
        running,
        stopping,
        stopped
    };

    void set_state(const state_v state)
    {
        std::unique_lock<std::mutex> lock{ m_mtx };
        m_state = state;
        m_condition_variable.notify_all();
    }

    void wait_for_state(const state_v state)
    {
        std::unique_lock<std::mutex> lock{ m_mtx };

        m_condition_variable.wait(lock, [&] { return m_state == state; });
    }

    void loop()
    {
        set_state(state_v::running);
        std::cout << "loop started\n";

        while (true)
        {
            std::unique_lock<std::mutex> lock{ m_mtx };

            // wait for loop time or change to stopping state
            // condition variable wait will not consume CPU cycles
            // for now delay is 1 second (this would have been one of your sleeps)
            auto wait_result = m_condition_variable.wait_for(lock, 1s, [&] { return m_state == state_v::stopping; });

            // if stopping then break the loop
            if (wait_result) break;

            // do your loop stuff here
            std::cout << ".";
        }

        set_state(state_v::stopped);
        std::cout << "\nloop stopped\n";
    }

    std::mutex m_mtx;
    std::condition_variable m_condition_variable; // despite its name condition variable is more of a signal
    state_v m_state{ state_v::idle };
    std::thread m_thread;
};

int main()
{
    gwai_t gwai;

    std::cout << "main start\n";
    std::cout << "press enter to end loop\n";

    gwai.start(); // start loop
    std::this_thread::sleep_for(5s); // simulate maintrhead doing all kind of things
    gwai.stop(); // and let gwai cooperatively stop background thread and synchronize with that stopping.
    
    std::cout << "main exit\n";
}

相关问题