Visual C++/CLI 中的异步睡眠,如何创建一个 X 毫秒来调用函数,而不会使 GUI 也停止

Asynchronous Sleep in Visual C++/Cli, How to create a of X milliseconds to call a function without making the GUI stops too

本文关键字:调用 函数 GUI 一个 异步 CLI C++ 创建 Visual 何创建      更新时间:2023-10-16

标题很清楚,我只需要知道是否有办法休眠几毫秒,然后在不停止 GUI 的情况下调用函数。我现在所做的方式只是添加Sleep(3000);,这使得 GUI 冻结,直到睡眠时间完成。

我尝试在线程中添加Sleep()然后调用函数,但它不起作用,因为该函数在 GUI 元素中进行修改,而线程不允许您访问 GUI 元素(如标签、按钮......

那么我应该怎么做才能让我的代码等待几秒钟继续,但不停止 GUI?

顺便说一句,睡眠在backgroundWorker_RunWorkerCompleted内(这只是后台工作者完成后后台工作者的回调)。

void backgroundWorker_RunWorkerCompleted(Object^ /*sender*/, RunWorkerCompletedEventArgs^ e){
     printf("Done, Waiting 3 seconds to do something that has to be 3 seconds after.");
     Sleep(3000); //at this point the UI freezes
     this->CallFunction();     
}

关于如何做到这一点的任何提示/建议?

你提到了一个GUI,但没有提到任何特定的UI框架。我暂时假设WinAPI,但从概念上讲,绝大多数其他框架都有我将要提到的内容。

在 Win32 中,可以使用计时器来实现所需的目标。在您的WM_CREATEWM_INITDIALOG等中,使用设置计时器设置计时器。然后,系统会获取您提供的超时值,每次该值经过时,它都会通过WM_TIMER消息通知您的窗口。 SetTimer会立即返回,因此不会阻止您的 UI 线程。

另一种方法是在开始时启动一个新线程。这个新线程将进行睡眠,并每 3 秒完成一次需要完成的任何工作。由于它是在单独的线程中完成的,因此 UI 不会锁定。您应该始终从WndProc快速/立即返回。如果需要做很多工作,则应将该工作委托给另一个线程,该线程将完成繁重的工作。

让你的后台线程在主线程上为一个互斥变量设置一个标志,告诉它(主线程)继续做它正在做的事情是安全的。这有点像红绿灯——所有角落(两侧)都有相互关联的灯。

您的主线程不会暂停,但您可以在其中处理(gui 和其他)消息的 while 循环,但在设置互斥变量之前它不会中断循环——这将发生在后台线程上。

以下是一些帮助您入门的参考资料:

  • C++中的锁和互斥锁
  • 互斥示例/教程?

编辑 -- 根据请求:

全局定义互斥句柄和变量:

HANDLE sharedMemoryMutex = CreateMutex(NULL, FALSE, "My mutex =)!");
BOOL good2Go = false;

然后,在您的后台线程中,它将如下所示:

void wait() {
    // First, we sleep...
    sleep(3000);
    // Now, we request a lock on the good2Go variable, so that when we write to
    // it, no other thread is:
    DWORD dwWaitResult = WaitForSingleObject(sharedMemoryMutex, INFINITE);
    if (dwWaitResult == WAIT_OBJECT_0 || dwWaitResult == WAIT_ABANDONED) {
    if (dwWaitResult == WAIT_ABANDONED) {
            // Shared memory is maybe in inconsistent state because other program
            // crashed while holding the mutex. Check the memory for consistency
            ...
    }
    // Access your shared memory
    good2Go = true;
    // Release the lock so that the main thread can read the variable again..
    ReleaseMutex(sharedMemoryMutex);
}

然后在您的主线程中,它将看起来像这样:

// Create your background wait thread:
// .....
while(true) {
    // Handle messages so that your program doesn't appear frozen.
    // You will have to do your research on this one =)
    YourMessageHandler();
    // Request a lock on the good2Go variable, so we can read it w/o corrupting it
    DWORD dwWaitResult = WaitForSingleObject(sharedMemoryMutex, INFINITE);
    if (dwWaitResult == WAIT_OBJECT_0 || dwWaitResult == WAIT_ABANDONED) {
    if (dwWaitResult == WAIT_ABANDONED) {
            // Shared memory is maybe in inconsistent state because other program
            // crashed while holding the mutex. Check the memory for consistency
            ...
    }
    // Exit the loop
    if( good2Go ) { ReleaseMutex(sharedMemoryMutex); break; }
    ReleaseMutex(sharedMemoryMutex);
}
// Continue your code after the 3 pause....

如果您还不知道,则必须弄清楚如何处理消息。此外,您的互斥体实现可能有所不同,但是您说您使用的是VC,因此上面的win32实现应该没问题。我想你明白一般概念

由于您使用的是 VC++/CLI,因此它是 C# 的近亲。您应该查看此处以了解如何从另一个线程更新 GUI:

如何在 C# 中从另一个线程更新 GUI?

快速修复:将Sleep调用放在BackgroundWorker任务中,而不是RunWorkerCompleted事件处理程序中。 事件处理程序在 GUI 线程上运行。

更好的解决方法:了解像迈克建议的计时器。 WinForms 提供了计时器,这些计时器将在您选择的延迟后触发事件。 启动后台任务只是为了让它进入睡眠和退出是一种可怕的浪费。