在 Windows 中创建 C++ 非阻塞计时器

Posted

技术标签:

【中文标题】在 Windows 中创建 C++ 非阻塞计时器【英文标题】:Creating a C++ Non-Blocking Timer in Windows 【发布时间】:2012-05-27 22:26:01 【问题描述】:

在 Linux 上,我会使用 fork() 创建一个子进程,这将是我的倒计时计时器,一旦计时器结束,子进程将向父进程发送一个信号,告诉它计时器已结束.然后父进程应该相应地处理信号。

我不知道如何在 Windows 上执行此操作。这里有些人建议使用线程,但他们从未编写任何示例代码来说明如何做到这一点。

最重要的是计时器是非阻塞的,这意味着它在后台保持倒计时,而程序正在接受用户的输入并正常处理。

你能告诉我怎么做吗?

编辑:

该应用程序是一个控制台。请给我看示例代码。谢谢!

更新:

所以在我阅读了这里的一些建议后,我在这里搜索了一些答案,发现这个one 很有帮助。

然后我写了下面的代码,它可以工作,但不是应该的:

#include <Windows.h>
#include <iostream>
#include <string>
using namespace std;

#define TIMER_VALUE (5 * 1000) //5 seconds = 5000 milli seconds
HANDLE g_hExitEvent = NULL;

bool doneInTime = false;
string name;

bool inputWords();


//The below function will be called when the timer ends
void CALLBACK doWhenTimerEnds(PVOID lpParameter, BOOLEAN TimerOrWaitFired)

    if(!doneInTime)
    
        cout << "\nOut of time ... try again ..." << endl;
        name = "";
        doneInTime = inputWords();
    
    SetEvent(g_hExitEvent);



bool inputWords()


    /* doWhenTimerEnds() will be called after time set by 5-th parameter and repeat every 6-th parameter. After time elapses,
    callback is called, executes some processing and sets event to allow exit */
    HANDLE hNewTimer = NULL; 
    BOOL IsCreated = CreateTimerQueueTimer(&hNewTimer, NULL, doWhenTimerEnds, NULL, TIMER_VALUE, 0, WT_EXECUTELONGFUNCTION);

    g_hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    cout << "Input your name in 5 seconds .. " << endl;
    std::getline(cin, name);

    DeleteTimerQueueTimer(NULL, hNewTimer, NULL);
    return true;


int main()

    doneInTime = inputWords();
    cout << "Hello, " << name << "! You're done in time" << endl;

    //WaitForSingleObject(g_hExitEvent, 15000);

    system("pause");
    return 0;

问题是,被中断的 getline() 永远不会停止,随后的 getline() 甚至会读取之前输入的文本!请问我该如何解决?如果有更好的方法,请您指出来吗?

【问题讨论】:

重要问题:您的应用程序是控制台应用程序还是 GUI 应用程序?答案会大不相同。 C++11 有std::thread,winapi 有CreateThread。其中任何一个都应该相对容易找到示例。 在 Linux 下这是一个糟糕的方法,你在这里得到了不好的建议。倒计时期间,除了等待控制台输入之外,您还在执行哪些任务?倒计时是否需要中断任务,或者任务会轮询以查看倒计时是否已经过去,或者......? 只是要求用户在特定的时间内输入一些东西,如果允许的时间过去了,那么程序将停止接受用户的输入并询问他是否重新启动或做其他事情.. 【参考方案1】:

这是一个适用于 Windows API 的示例:

#include <windows.h>
#include <iostream>

DWORD WINAPI threadProc()

    for (int i = 0; ; ++i)
    
        std::cout << i << '\n';
        Sleep (1000);
    

    return 0;


int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                    LPSTR lpszCmdLine, int iCmdShow)

    CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)threadProc, NULL, 0, NULL);

    int i;
    std::cin >> i;

    return ERROR_SUCCESS;

基本上,主函数创建一个使用过程threadProc 执行的线程。您可以将threadProc 视为线程。一旦结束,线程就结束了。

threadProc 大约每秒输出一次运行计数,而主函数等待阻塞输入。一旦给出输入,整个事情就结束了。

还要注意CreateThread 与最少的参数一起使用。它返回一个线程句柄,您可以在 WaitForSingleObject 等函数中使用该句柄,最后一个参数可以接收线程 ID。

【讨论】:

好的,谢谢你的代码几乎可以工作了。我只需将int WINAPI WinMain (HINST... 更改为int main() 即可运行它。计时器正在计数并打印输出,而不会阻塞在 cin 处等待输入的 main。现在我需要在线程和主线程之间进行通信,例如,一旦计数器达到 0,就向主线程发送信号或其他东西以停止读取用户的输入,并将程序重定向到其他地方。谢谢 @AhmedFakhry,首先,这是 Windows 应用程序入口点的官方签名。我怀疑您没有 Windows GUI 项目。对于像输入这样的阻塞过程,您可能最好将输入放在另一个线程中,然后在您愿意时终止该线程。这是我能想到的唯一真正的方法来阻止它在输入中间。也就是说,您可以使用SetTimer 之类的方法和您自己的回调更轻松地做到这一点。指定时间,一旦计时器到期,它就会调用您的函数。请参阅 gahcep 对该方法的回答。 你能看看我更新的问题,看看你能不能帮忙?【参考方案2】:

您可以在线程内或线程外使用Waitable Timer Objects。要使用这种对象,只需使用 SetWaitableTimer 函数。根据 MSDN 的函数定义:

激活指定的等待计时器。当到期时间到达时,定时器会收到信号,设置定时器的线程会调用可选的完成例程。

【讨论】:

【参考方案3】:

如果您为它们实现 kbhit() 函数,这是另一个跨平台工作的解决方案:

#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#if defined(WIN32)
    #include <conio.h>
#else
    // kbhit() implementation for other platforms
#endif

boost::mutex    mutex_;
bool            timeExpired_ = false;

void threadFunc()

    while(1) 
        
            boost::mutex::scoped_lock lock(mutex_);
            if (timeExpired_)
                break;
            #if defined(WIN32)
                kbhit();
            #endif
        
        boost::this_thread::sleep(boost::posix_time::milliseconds(1));
    


int main(int argc, char *argv[])

    boost::thread worker(threadFunc);

    worker.timed_join(boost::posix_time::milliseconds(5000));

    
        boost::mutex::scoped_lock lock(mutex_);
        timeExpired_ = true;
    

    worker.join();

    return 0;

这种方法使用 boost::thread 并在创建线程后等待 5 秒来设置过期标志,然后再次等待线程,直到它完成其功能。

【讨论】:

不幸的是它不知道如何在 Windows 上导入这些库:【参考方案4】:

寻找相同问题的解决方案,我发现这篇文章很有帮助:

http://www.codeproject.com/Articles/1236/Timers-Tutorial

您似乎已经知道,最好的答案(在 Windows 2000 之后的世界中)似乎是队列计时器,就像 CreateTimerQueueTimer。

【讨论】:

【参考方案5】:

试试这个:

//Creating Digital Watch in C++
#include<iostream>
#include<Windows.h>
using namespace std;

struct time

int hr,min,sec;
;
int main()

time a;
a.hr = 0;
a.min = 0;
a.sec = 0;

for(int i = 0; i<24; i++)

    if(a.hr == 23)
    
        a.hr = 0;
    

    for(int j = 0; j<60; j++)
    
        if(a.min == 59)
        
            a.min = 0;
        

        for(int k = 0; k<60; k++)
        
            if(a.sec == 59)
            
                a.sec = 0;
            

            cout<<a.hr<<" : "<<a.min<<" : "<<a.sec<<endl;
            a.sec++;
            Sleep(1000);
            system("Cls");
        
    a.min++;



    a.hr++;



【讨论】:

以上是关于在 Windows 中创建 C++ 非阻塞计时器的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用 Boost Signals2 和 Threads 在 C++ 中创建软件看门狗定时器线程吗?

如何使用 C++ 在 Windows 中创建守护线程?

如何在 Visual Studio 2019 C++ 中创建非虚拟文件夹

如何在 Windows ( C++ ) 中创建进程以运行另一段代码?

非阻塞 Websockets C++ 奇怪的延迟

指针对象在 Visual C++ 6.0 中创建 windows 错误