Visual C++/Cli 中的异步睡眠,如何在不使 GUI 停止的情况下创建一个 X 毫秒来调用函数
Posted
技术标签:
【中文标题】Visual C++/Cli 中的异步睡眠,如何在不使 GUI 停止的情况下创建一个 X 毫秒来调用函数【英文标题】:Asynchronous Sleep in Visual C++/Cli, How to create a of X milliseconds to call a function without making the GUI stops too 【发布时间】:2012-04-10 14:06:26 【问题描述】:标题很清楚,我只需要知道是否有一种方法可以休眠几毫秒,然后在不停止 GUI 的情况下调用函数。我现在的做法是添加 Sleep(3000);
,这会使 GUI 冻结,直到睡眠时间结束。
我尝试在线程中添加Sleep()
,然后调用一个函数,但它不起作用,因为该函数在 GUI 元素中进行了修改,而线程不允许您访问 GUI 元素(如标签, 按钮...)。
那么我应该怎么做才能让我的代码等待几秒钟才能继续,但不停止 GUI?
顺便说一句,Sleep 在 backgroundWorker_RunWorkerCompleted
内(这只是 backgroundworker 完成后的回调)。
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();
关于如何做到这一点的任何提示/建议?
【问题讨论】:
【参考方案1】:您提到了一个 GUI,但没有提到任何特定的 UI 框架。我暂时假设 WinAPI,但从概念上讲,绝大多数其他框架都有我将要提到的内容。
在 Win32 中,您可以使用计时器来实现您想要的。在您的WM_CREATE
或WM_INITDIALOG
等设置一个带有SetTimer 的计时器。然后系统会获取您提供的超时值,并且每次该值过去时,它都会通过WM_TIMER 消息通知您的窗口。 SetTimer
立即返回,因此不会阻塞您的 UI 线程。
您可以这样做的另一种方法是在开始时启动一个新线程。这个新线程将进行睡眠并每 3 秒执行一次需要完成的任何工作。由于它是在单独的线程中完成的,因此 UI 不会锁定。您应该始终从WndProc
快速/立即返回。如果需要完成大量工作,则应将该工作委托给另一个线程来完成繁重的工作。
【讨论】:
【参考方案2】:让你的后台线程为你的主线程上的一个互斥变量设置一个标志,告诉它(主线程)继续做它正在做的事情是安全的。它有点像一个红绿灯——所有角落(两侧)都有相互关联的灯。
您的主线程不会暂停,但您可以有一个 while 循环来处理(gui 和其他)消息,但在设置互斥变量 get 之前它不会中断循环——这将发生在后台线程上.
以下是一些帮助您入门的参考资料:
Locks and Mutexes in C++ Mutex example / tutorial?编辑 -- 每个请求:
在全局范围内,定义您的互斥量句柄和变量:
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 实现应该没问题。而且我认为您了解一般概念
【讨论】:
嗯,你能用一段代码告诉我你的意思吗?为了更好的可视化?而且我在我的代码中使用互斥锁让另一个线程在更改相同变量时等待,但我不明白如何在我的示例中的 Visual C++/CLI 中执行此操作。给我看一些代码示例。【参考方案3】:由于您使用的是 VC++/CLI,因此它是 C# 的近亲。您应该在这里查看如何从另一个线程更新 GUI:
How to update the GUI from another thread in C#?
【讨论】:
【参考方案4】:快速修复:将您的 Sleep
调用放在 BackgroundWorker
任务中,而不是在 RunWorkerCompleted
事件处理程序中。事件处理程序在 GUI 线程上运行。
更好的解决方法:了解 Mike 建议的计时器。 WinForms 提供了计时器,这些计时器将在您选择的延迟后触发事件。启动后台任务只是为了让它休眠并退出是一种可怕的浪费。
【讨论】:
以上是关于Visual C++/Cli 中的异步睡眠,如何在不使 GUI 停止的情况下创建一个 X 毫秒来调用函数的主要内容,如果未能解决你的问题,请参考以下文章
使用平台工具集 v120 (Visual Studio 2013) 在 Visual Studio 2015 中创建 C++/CLI 项目