延迟WM_CLOSE以便我可以允许线程正常退出 [英] Delaying WM_CLOSE so I can allow threads to exit gracefully

查看:55
本文介绍了延迟WM_CLOSE以便我可以允许线程正常退出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,尊敬的CodeProject专业人士。



我有以下问题:



我需要创建一个无模式对话框,当按下其中一个按钮时启动一个线程。



该按钮将被隐藏,直到线程结束。然后它会显示出来。



线程执行冗长的工作(填充Excel),所以当我关闭我的主应用程序时,虽然我的对话框和线程关闭,留下Excel僵尸进程



我从这里使用了MSDN示例:http://support.microsoft.com/kb/216686 [ ^ ]和此处的CodeProject文章 MS使用C ++的Office OLE自动化 [ ^ ],学习OLE自动化。



我发现了非常类似的问题,这帮助我从这里的线程开始当对话框正确关闭线程点击关闭按钮 [ ^ ],我也使用了一个关于Petzold的书编程Windows第5版的线程的例子。



到目前为止,它似乎一切都很好,除了带下划线的部分。



我尝试将WM_CLOSE消息发送到对话框以触发线程堕胎处理程序,但它没有工作。



如果我在主窗口的WM_CLOSE处理程序中添加一个消息框,在将WM_CLOSE发送到对话框后,所有线程都正常中止,我知道因为我可以看到Excel在TaskManager中关闭,因为我等待它们,然后单击消息框确定按钮。



我的问题是:



如何取消消息框,如何延迟关闭主窗口,这样我的工作线程可以优雅地中止?



谢谢。



如果需要代码片段,我会提供。我已经省略了这篇文章。



编辑#1:




我已设法实现正确的线程功能。



上面填写Excel的文章中的代码被插入到线程函数中。



在每个指令块之后,轮询一个布尔变量以查看是否应该中止线程函数。



在线程函数中,持有该布尔变量的数据结构被声明为volatile,因此编译器无法优化代码,这将导致使用if语句替换while循环。



此解决方案的灵感来自 Charles Petzold的传奇书籍编程Windows第5版



也许我可以使用事件对象,因为它更适合这里,但在这一点上我会满足于这个解决方案,因为我有时间麻烦。



线程句柄声明为静态,并在WM_INITDIALOG中初始化为NULL。



我已经定义了2个自定义消息,因此如果发生错误或执行了干净退出,则线程函数可以通知对话框。 />


这些定义如下:

Hello, respected professionals from CodeProject.

I have a following problem:

I need to create a modeless dialog box, which starts a thread when one of its buttons is pressed.

That button will be hidden, until thread finishes. Then it will be shown.

Thread does lengthy job ( populates Excel ), so when I close my main app, although my dialog box and thread closes, Excel "zombie" process is left.

I have used MSDN example from here: http://support.microsoft.com/kb/216686[^] , and CodeProject article from here MS Office OLE Automation Using C++[^] , to learn OLE Automation.

I have found very similar question which helped me to start with threads here Abort thread properly when dialog box close button is clicked[^] , and I have also used an example about threads from Petzold's book Programming Windows 5th edition.

So far, it seems that everything works great, except for the underlined part.

I have tried sending the WM_CLOSE message to the dialog box to trigger thread abortion handler, but it doesn't work.

If I add a message box to the main window's WM_CLOSE handler, after sending the WM_CLOSE to the dialog box, all threads abort gracefully, I know that because I can see Excel closing in TaskManager, since I wait for them and only then click the message boxes OK button.

My question is:

Instead of a message box, how can I delay the closing of the main window, so my worker threads can abort gracefully ?

Thank you.

If code snippets are required, I will provide them. I have omitted them to keep this post short.

EDIT#1:


I have managed to implement proper thread function.

Code from the article above that fills Excel is inserted into thread function.

After each block of instructions a boolean variable is polled to see if thread function should be aborted.

In the thread function, data structure holding that boolean variable, is declared volatile, so compiler can not optimize the code, which would result in replacing while loop with if statement.

This solution is inspired by the example from Charles Petzold's legendary book, "Programming Windows" 5th edition.

Perhaps I could use event object, as it fits here better, but at this point I will satisfy myself with this solution, since I'm in a time trouble.

Thread handle is declared static, and is initialized to NULL in WM_INITDIALOG.

I have defined 2 custom messages, so thread function can inform dialog box if error occurred or a clean exit was performed.

These are defined as bellow:

#define WM_THREAD_OK    ( WM_APP +1 )

#define WM_THREAD_ERROR ( WM_APP + 2 )





在我的对话框中,这些消息中的任何一个都显示以前隐藏的保存按钮(记住,为了避免多个线程被触发,我在用户按下并启动线程时隐藏了按钮) 。



WM_CLOSE和IDCANCEL处理程序是相同的,实现方式如下:





In my dialog box, either of these messages shows previously hidden Save button ( remember, to avoid multiple threads being fired off, I have hidden the button when user presses it and launches a thread ).

WM_CLOSE and IDCANCEL handlers are identical, and are implemented like this:

case WM_CLOSE:

    if( threadHandle ) // thread is alive
    {
       obj.bContinue = false; // send abortion flag

       WaitForSingleObject( threadHandle, INFINITE );

       CloseHandle( threadHandle );

       threadHandle = NULL;

    }

    DestroyWindow(hwnd);

    return TRUE;
    break;





我现在需要做的一切,是重写我的按钮处理程序。



我需要在启动新线程之前将线程句柄设置为NULL。



我的问题是:



我应该将句柄设置为NULL以响应WM_THREAD_OK消息,如下所示:





All I need to do now, is to rework my button handler.

I need to set thread handle to NULL, before I launch a new thread.

My question is:

Should I set the handle to NULL in response to WM_THREAD_OK message like this:

case WM_THREAD_OK: // thread sent this

    if( threadHandle ) // just wait until it fully exits
    {
       WaitForSingleObject( threadHandle, INFINITE );

       CloseHandle( threadHandle );

       threadHandle = NULL;
    }

    ShowWindow( GetDlgItem( hwnd, IDC_BUTTON1 ), SW_SHOW );

    return TRUE;





或者应该我在我的按钮处理程序中这样做:





or should I do it in my button handler like this:

case IDC_BUTTON1:
  {
    ShowWindow( GetDlgItem( hwnd, IDC_SAVE ), SW_HIDE ); // hide button

    // fill the structure passed to thread function

    td.hwnd = hwnd;

    td.PrimaryKey = primaryKey;

    // create thread

    DWORD threadID;


    /*** THIS IS THE PART WHERE I NULL THE THREAD HANDLE ***/

    if( threadHandle ) // if thread is alive
    {
       // wait for it to finish

       WaitForSingleObject( threadHandle, INFINITE );

       // then NULL out the handle

       CloseHandle( threadHandle );

       threadHandle = NULL;
     }

     /********  AND THEN CREATE NEW THREAD *********/


     threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, 
                                        (void*)&td, 0, &threadID );

     if( !threadHandle )
     {
        MessageBox( hwnd, L"Error", L"Error", MB_ICONERROR );

        DestroyWindow(hwnd);
     }

  }
  break;





这是唯一剩下的东西,因为我相信现在一切都处于良好状态。



我欢迎建设性意见/评论。



谢谢。



That is the only thing left, since I believe everything is in fine order now.

I would welcome a constructive advice/comment.

Thank you.

推荐答案

当显示无模式对话框时,不允许关闭应用程序。



好​​吧,设置一个标志,表示你要关闭应用程序并告诉无模式对话框关闭(而且这个也设置了一个标志)并告诉线程停止(最好再通过一个标志)。



线程退出时,关闭无模式对话框,关闭无模式对话框后关闭应用程序。 />




我很长一段时间没有在API lvel工作过。在.NET环境中,这些内容实现起来相对简单,因此在您的情况下可能会有点困难,因为您必须在该级别处理更多的subtildétails。
Don't allows to close the application when modeless dialog is displayed.

Well, set a flag to indicate that you want to close the application and tell the modeless dialog to close (and this one also set a flag instead) and tell the thread to stop (preferably through a flag again).

When the thread exit, then close the modeless dialog and once the modeless dialog is closed the close the application.


I have not worked at the API lvel since a very long time. In .NET environment, those things are relatively trivial to implement thus it might be a bit harder in your case as you have to handle more subtil détails at that level.


每个线程都应该有一个API设置一个布尔值(或事件或有限状态机状态或等效),告诉线程您希望它关闭。任何线程终止逻辑都应该由线程本身处理。线程的正常处理逻辑应该定期轮询终止请求并采取相应的行动。



当你从外部进程中不正常地终止线程时,你不给线程一个机会去除资源或正确清理自己。很多人在外部杀掉线程,但我认为这有缺陷的行为。
Each of your threads should have an API that sets a boolean (or event or finite state machine status or equiv) that tells the thread that you want it to shut down. Any thread termination logic should really be handled by the thread itself. The thread's normal processing logic should poll the termination request periodically and act accordingly.

When you terminate a thread ungracefully from an external process, you do not give the thread an opportunity to deaccess resources or clean itself up properly. A lot of people kill off threads externally but I consider this flawed behavior.


AlwaysLearningNewStuff写道:
AlwaysLearningNewStuff wrote:

...没有关于对话框向工作线程发送消息的说法

…there was no saying about dialog box sending message to a worker thread

首先,你不能只是向线程发送消息。



线程你想发送消息两个应该定义一些接受消息的机制。并且消息队列可以像仅接受一条消息的字段一样简单(例如它是终止时间,新数据可用)。特别是,它应该是循环的,在每次迭代时包含一些循环检查消息可用性。它可能看起来像轮询,而且往往是,但情况并非总是这样。特别是,您可以开发机制,以便浪费零CPU时间等待。当没有消息时,您可以限制线程并使其保持等待状态。这可以使用事件对象来完成。请参阅:

http ://msdn.microsoft.com/en-us/library/windows/desktop/ms682655%28v=vs.85%29.aspx [ ^ ],

< a href =http://msdn.microsoft.com/en-us/library/windows/desktop/ms686915%28v=vs.85%29.aspx> http://msdn.microsoft.com/en-us /library/windows/desktop/ms686915%28v=vs.85%29.aspx [ ^ ]。



现在,最常见和或多或少的通用通信方法将是阻塞消息队列,顺便说一句,它可以允许您避免锁定对共享数据的访问(简而言之,因为事件对象本身就是一个线程同步原语)。这样的队列是生产者 - 消费者问题的主要方面之一:

https ://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem [ ^ ]。



在Windows上,Microsoft Asynchronous Agents Library提供了一种全面的方法/ i>: http://msdn.microsoft.com/en- us / library / dd492627%28v = vs.110%29.aspx [ ^ ]。



为了您的目的,有可能是这样全面的方法会有点矫枉过正。毕竟,您可能只在一个实例中发送消息就可以解决您的问题(但是当您在前一个消息尚未消耗时发送新消息时,您应该彻底解决该方案,因此您需要建立一种新机制吸收旧机制的机制。同步将通过事件对象完成。您可能还需要锁定,在Windows上应该由 critical section 表示,因为您只想在同一进程上同步线程;和关键部分是互斥体的最轻量级变体(请参阅 http://en.wikipedia。 org / wiki / Mutual_exclusion [ ^ ]。)



我还建议开发一个线程包装器,在封装类中封装诸如访问和锁定之类的东西。通过这种方式,您可以抽象出对线程的访问权限。



-SA

First of all, you cannot just "send message" to a thread.

The thread you want to "send message" two should define some mechanism to accept message. And the message queue could be as simple as the field accepting only one message (such as "it's time to terminate", "new data is available"). In particular, it should be circular, contain some loop checking message availability on each iteration. It may look like polling and often is, but this is not always the case. In particular, you may develop the mechanism the way to waste zero CPU time for waiting. You can throttle the thread and keep it in a wait state when there are no messages. This can be done using the event object. Please see:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655%28v=vs.85%29.aspx[^],
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686915%28v=vs.85%29.aspx[^].

Now, most usual and more or less general communication method would be a blocking message queue which, by the way, may allow you to avoid locking of the access to shared data (in short, because event object is itself a thread synchronization primitive). Such queue is one of the main aspects of the producer-consumer problem:
https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem[^].

On Windows, one comprehensive approach is offered by Microsoft Asynchronous Agents Library: http://msdn.microsoft.com/en-us/library/dd492627%28v=vs.110%29.aspx[^].

It is possible that, for your purpose, such comprehensive approach would be an overkill. After all, it's possible that you may solve your problems with sending a "message" in only one instance (but you should thoroughly work out the scenario when you send a new message when the previous one is not yet consumed, so you would need to develop a mechanism where new one absorbs the old one). The synchronization would be done via an event object. You may also need locking, which on Windows should be represented by critical section as you want to synchronize the threads only on the same process; and critical section is the most light-weight variant of mutex (please see http://en.wikipedia.org/wiki/Mutual_exclusion[^]).

I would also advise development of a thread wrapper, to encapsulate such things as access and locking inside the wrapper class. This way, you can abstract out access to the thread.

—SA


这篇关于延迟WM_CLOSE以便我可以允许线程正常退出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆