如何暂停主线程直到第二个线程的终止?<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
来自 http://www.codeproject.com/threads/waitthreadmsi.asp#xx441032xx
By Ybbozman
不要被线程这个词给吓坏了,留下来先看看吧;)
作为翻译这篇短文的我在VC++开发中遇到了与 Ybbozman 相类似的问题,在主应用程序中(主进程-其实还是线程,称主线程也可),点击“查找”按钮时,我作了一个搜索的递归查找文件的事件-这个事件将非常慢,结果是导致界面重绘明显变慢、cpu资源占用率非常高,原因是windows抢先式多任务将此主线程的优先级置为最高,查找文件事件是作为这个主线程正在执行的事件,cpu大部分的时间片会交给查找文件事件,而界面重绘的操作速率将变得不可预知。
为这个查找文件事件启动一个新的工作线程,是非常高效的,查找文件事件在新的工作线程中工作,但优先级是不会高于当前主线程的,这就保证了界面重绘操作的正常进行,问题是我想在查找文件事件完成后再继续执行主线程的某些操作,这就涉及到线程之间等待-wait(称之为同步-Synchronization更好些)的问题,Ybbozman 的问题与我这个问题有些类似,下面看一下Ybbozman 是如何解决线程之间等待的问题。
简介
几周前,我需要制作一个对话框将它作为MSI安装程序(Windows2000的软件安装)的一部分,为此所写的代码必须是作为一个Regular(规则)DLL发布。这个对话框是否可见,完全依赖于安装完成的结果,即该功能依赖于另一个线程(在这里是正在进行的安装过程的工作线程)完成,安装完成后这个线程被终止,并最终显示这个对话框。
解决
你必须写两个函数,第一个void CMyTestDialog::PeekMessageLoop() 将从消息队列中获取消息:
void CMyTestDialog::PeekMessageLoop()
{
MSG msg;
while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
第二个, void CMyTestDialog::WaitForThreadToTerminate(HANDLE hThread) 将指示哪个线程需要等待并作进一步处理:
void CMyTestDialog::WaitForThreadToTerminate(HANDLE hThread)
{
DWORD dwRet;
do
{
dwRet = ::MsgWaitForMultipleObjects(1, &hThread, FALSE,
INFINITE, QS_ALLINPUT);
if (dwRet != WAIT_OBJECT_0)
{
PeekMessageLoop();
}
} while ((dwRet != WAIT_OBJECT_0) && (dwRet != WAIT_FAILED));
}
示例代码
假设对话框上有一个按钮,当点击按钮时,开始启动第二个线程,等到第二个线程完成后,我们再往下继续主线程:
void CMyTestDialog::OnButton1()
{
m_pUpdateThread = AfxBeginThread(UpdateDeviceContent,
(LPVOID)this/*, THREAD_PRIORITY_BELOW_NORMAL*/);
if (m_pUpdateThread)
{
WaitForThreadToTerminate(m_pUpdateThread->m_hThread);
}
//Do whatever you want after the action is finished
}
Ybbozman 在解决这个问题时用到了线程之间同步的API函数::MsgWaitForMultipleObjects 函数,它是用于等待某个对象(在这里是要等待的线程T2)的信号态-Semaphore(注1)同步函数,当主线程T所等待的线程T2信号态到达时(利用特定的消息-message指示信号态的到达,将返回WAIT_OBJECT_0,::MsgWaitForMultipleObjects的最后一个参数QS_ALLINPUT指定了所有输入消息类型都可作为信号态到达并因此返回,PeekMessageLoop()将当前运行线程中的任何一个窗口消息以及由PostThreadMessage递交到当前运行线程中的所有消息从消息队列中移除,因此,迭代再次调用::MsgWaitForMultipleObjects时,只有新的消息到达后该函数才返回;此外,PeekMessageLoop()调用后,迭代再次调用::MsgWaitForMultipleObjects之前,会处理一些系统事件:例如,有关窗体重绘的WM_PAINT消息(注意:即使PeekMessage最后一个参数设为PM_REMOVE,PeekMessageLoop()调用后,WM_PAINT消息也不会从消息队列中移除)。
讲了许多,我都有点晕了,再归纳一下:
一. ::MsgWaitForMultipleObjects迭代等待指定线程中的特定消息,当线程结束时,::MsgWaitForMultipleObjects将返回WAIT_OBJECT_0以表示线程的结束!
二. PeekMessageLoop迭代获得当前运行线程中的任何一个窗口消息以及由PostThreadMessage递交到当前运行线程中的消息,调用PeekMessageLoop后将消息从消息队列中移除, 其目的有两个:
a. 再次调用::MsgWaitForMultipleObjects时必须等到一个新的消息到达后才返回;
b. 更新系统事件,这是很有用的功能,这将防止在消息等待过程中(即迭代::MsgWaitForMultipleObjects)独占CPU资源,而不处理某些重要的系统事件:诸如:硬件输入消息,窗口重绘,定时消息等等。
MsgWaitForMultipleObjects支持下面对象的同步:
Mutex 互斥
Event 事件
Semaphore 信号
Process 进程
Thread 线程
Critical section 临界段
此外,线程之间的同步函数还有WaitForSingleObject, WaitForMultipleObjects,使用方法大同小异,它们除了不支持Critical section(临界段)。而MsgWaitForMultipleObjects更多地用于等待来自于同步对象中某些特殊的消息。
最后,文中错误难免,请有识之士批评指正。
注1:荷兰著名计算机科学家Dijkstra将进程或线程的同步问题抽象为信号态,Dijkstra已于2002年8月6号在荷兰去逝。
参考:MSDN帮助文档。
分享到:
相关推荐
接下来,我们讨论第二个案例:实现线程同步。在多线程环境中,当多个线程访问共享资源时,如果没有适当的同步机制,可能会导致数据不一致或竞态条件。Java提供了多种同步工具,如`synchronized`关键字、`wait()`, `...
以上就是C#多线程学习的第二部分,重点介绍了如何创建和操纵一个线程。通过上述的知识点,可以对C#中的多线程操作有更深的理解和实践。在多线程编程实践中,合理的设计和使用线程能够极大提升应用程序性能和用户体验...
第二个参数是一个pthread_attr_t类型的指针,指向一个线程属性对象,用于设置新线程的属性,通常为NULL,表示使用默认属性。第三个参数是一个函数指针,指向新线程将要执行的函数,该函数需要有特定的签名,即无参数...
对于耗时的计算或I/O操作,可以将其放在后台线程中执行,这样主线程(通常是UI线程)可以继续响应用户的交互,提供更好的用户体验。 #### 三、Java线程的创建方式 **3.1 继承Thread类** 这是创建线程最常见的方式...
- 每个C#程序至少有一个主线程,它是程序启动时的第一个线程。 2. **创建线程** - 在C#中,可以通过`System.Threading.Thread`类来创建和控制线程。 - 示例代码展示如何获取当前线程的信息: ```csharp using ...
因此,一个进程可以包含多个线程,主线程是每个进程默认启动的线程,其他线程由主线程创建。 多线程允许程序同时执行多个任务,从而更好地利用CPU资源,简化编程模型,特别是处理异步事件时。线程比进程轻量级,...
在VB6.0中,主线程通常是UI线程,负责更新界面和处理用户输入。为了确保线程安全,非UI线程通常不应直接修改UI元素,而是通过事件或消息机制通知UI线程进行更新。 7. **实验文件解析**: 文件列表中,`...
接下来,我们来看第二个示例,这个示例更加复杂一些。它定义了一个名为`Go`的方法,并且在主线程和一个新创建的线程中同时调用该方法。方法`Go`中包含了一个局部变量`cycles`,用于输出一系列的问号。 ```csharp ...
- **主线程**:程序启动时默认创建的第一个线程,负责管理其他线程。 - **子线程**:由主线程或其他线程创建的线程。 - **守护线程(后台线程)**:守护线程主要用于执行后台任务,如日志记录等。当所有非守护线程...
- **主线程**:程序启动时默认的第一个线程,也是应用程序的主要执行线程,负责程序的核心逻辑。 **3. 前台线程与后台线程** - **前台线程**:程序正常运行所必需的线程,如果所有前台线程都结束,则程序也随之...
- 多线程技术允许应用程序中存在多个线程,以提高CPU的使用效率,防止主线程被阻塞。 - 所有可能阻塞主线程的任务(如网络请求等)都不应在主线程中执行,但所有与用户界面相关的操作必须在主线程中完成。 #### ...
- 主线程:在进程入口执行的第一个线程被视为该进程的主线程。 - 组成部分:主要包括CPU寄存器、调用栈和线程本地存储器(TLS)。 - CPU寄存器:记录当前线程的状态。 - 调用栈:维护线程所调用的内存与数据。 ...
每个Java应用程序至少包含一个主线程,由JVM创建,用于执行`main()`方法。此外,JVM还会为诸如垃圾回收、对象终结等内部任务创建线程。 #### 知识点三:线程的生命周期 线程的生命周期经历了新创建、可运行、运行...
为了解决这个问题,Java提供了第二种创建线程的方法,即实现`Runnable`接口。这样,我们可以在不扩展`Thread`类的情况下创建线程,只需创建一个实现了`Runnable`接口的类,然后在`Thread`类的构造函数中传入这个`...
一旦轮到它来享用CPU资源时,就可以脱离创建它的主线程独立开始自己的生命周期了。另外原来处于阻塞状态的线程被解除阻塞后也将进入就绪状态。 3. **运行状态**:当就绪状态的线程被调度并获得处理器资源时,便...
`pthread_join()`函数在等待指定的线程终止时会阻塞调用它的线程,直到该线程结束。如果一个线程对多个线程多次调用`pthread_join()`,会造成逻辑错误。 3. 线程终止:线程可以通过`pthread_exit()`函数显式终止...
在Muti.java中,主线程调用h.start()启动新线程,两个线程并发执行,交替打印"Hello"和"你好",这就是线程并发的实际表现。 掌握这些知识点对于理解和编写高效的Java多线程程序至关重要。在实际开发中,合理地利用...
- **1.5.4 多线程和你的用户界面**:确保UI更新发生在主线程上,避免界面冻结。 - **1.5.5 了解线程退出时的行为**:正确处理线程的生命周期,避免内存泄漏。 - **1.5.6 处理异常**:设置异常处理器来捕获和处理线程...