Win32线程——等待另一个线程结束

简介: 转载:https://blog.csdn.net/yss28/article/details/53646627《Win32多线程程序设计》–Jim Beveridge & Robert Wiener “等待某个什么东西”是线程常常需要做的事。

转载:
https://blog.csdn.net/yss28/article/details/53646627


《Win32多线程程序设计》–Jim Beveridge & Robert Wiener

“等待某个什么东西”是线程常常需要做的事。等待是线程的“必要之恶”
如果你没有等待线程结束就莽撞地结束程序,线程会被系统强制结束掉——在它完成它的工作之前。
由于让线程停工是操作系统的责任,很合理地我们会认为操作系统也有责任让其他线程知道某个线程停工了。

Sleep()

这个函数要求操作系统中止线程动作,直到渡过某个指定时间之后才恢复。

#include <stdio.h>
#include <Windows.h>

DWORD WINAPI Thread(void *arg) {
    // doing something
    return 0;
}

int main(void) {
    HANDLE hThread = CreateThread(NULL, 0, Thread, NULL, 0, NULL);
    Sleep(?); // 不可能事先知道要等待Thread多久
    CloseHandle(hThread);
    return 0;
}

GetExitCodeThread()轮询检查

使用 GetExitCodeThread() 可以决定一个线程是否还在执行。

#include <stdio.h>
#include <Windows.h>

DWORD WINAPI Thread(void *arg) {
    // doing something
    return 0;
}

int main(void) {
    DWORD exitCode = 0;
    HANDLE hThread = CreateThread(NULL, 0, Thread, NULL, 0, NULL);
    while (1) {
        GetExitCodeThread(hThread, &exitCode); // 严重浪费 CPU 时间
        if (STILL_ACTIVE != exitCode)
            break;
    }
    CloseHandle(hThread);
    return 0;
}

WaitForSingleObject()

可看成一个新版的 Sleep() ,它能够在某个线程结束时(而不是某段时间结束时)被调用

可使用的核心对象有两种状态:激发未激发。WaitForSingleObject() 会在目标物变成激发状态时返回。

对象 说明
Thread(线程) 当线程结束时,线程对象即被激发。当线程还在进行时,则对象处于未激发状态。
Process(进程) 当进程结束时,进程对象即被激发。当进程还在进行时,则对象处于未激发状态。
Event Event 对象的状态直接受控于应用程序所使用的三个Win32函数:SetEvent()、PulseEvent()、ResetEvent()。CreateEvent()和OpenEvent()都可以传回一个event object handle。Event对象的状态也可以被操作系统设定。
Mutex 如果mutex没有被任何线程拥有,它就是处于激发状态。一旦一个等待mutex的函数返回了,mutex也就自动重置为未激发状态。
Semaphore Semaphore有点像mutex,但它有个计数器,可以约束其拥有者(线程)的个数。当计数器内容大于 0时,semaphore处于激发状态,当计数器内容等于0时,semaphore处于未激发状态。
#include <stdio.h>
#include <Windows.h>

DWORD WINAPI Thread(void *arg) {
    // doing something
    return 0;
}

int main(void) {
    HANDLE hThread = CreateThread(NULL, 0, Thread, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE); // 等待,直到线程被激发
    CloseHandle(hThread);
    return 0;
}

WaitForMultipleObjects()

允许你在同一时间等待一个以上的对象。你必须将一个由 handles 组成的数组交给此函数,并指定要等待其中一个对象或是全部的对象

(范例:保持线程池中始终有3个线程在运行)

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define POOL_SIZE 3 // 线程池大小
#define TASK_NUM 6

DWORD WINAPI ThreadFunc(LPVOID n) {
    srand(GetTickCount());

    Sleep(rand()%5000+500);
    printf("%d over\n", n);
    return ((DWORD)n);
}

int main(void){
    HANDLE hThrds[POOL_SIZE];
    int pIdx = 0, tIdx;
    DWORD rc;

    for (tIdx = 1; tIdx <= TASK_NUM; tIdx++) {
        if (tIdx > POOL_SIZE) {
            rc = WaitForMultipleObjects(POOL_SIZE, hThrds, FALSE, INFINITE); // 等待hThrds数组中任意一个变为激发状态,返回其索引
            pIdx = rc - WAIT_OBJECT_0;
            assert(pIdx >= 0 && pIdx < POOL_SIZE);
            printf("%d terminated\n", pIdx);
            CloseHandle(hThrds[pIdx]); 
        }

        hThrds[pIdx++] = CreateThread(NULL, 0, ThreadFunc, (LPVOID)pIdx, 0, NULL);
        printf("Thread #%d launched (pIdx %d)\n", tIdx, pIdx);
    }

    WaitForMultipleObjects(POOL_SIZE, hThrds, TRUE, INFINITE); // 等待hThrds数组中所有线程变为激发状态
    for (pIdx = 0; pIdx < POOL_SIZE; pIdx++)
        CloseHandle(hThrds[pIdx]);

    return EXIT_SUCCESS;
}

结果:
6

GUI 程序中等待

Windows 程序中的“主消息循环”看起来像这个样子:

while (GetMessage(&msg, NULL, 0, 0,))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

GetMessage() 有点像是特殊版本的 WaitForSingleObject(),它等待消息
不是核心对象。一旦你调用 GetMessage() ,除非有一个消息真正进入你的消息
队列( message queue )之中,否则它不会返回。

如果你在主线程中正使用 WaitForSingleO bject()或 WaitForMultipleObjects()等待某个对象被激发,你根本没有办法回到主消息循环中去。

为了解决这个问题,主消息循环必须修改,使它得以同时等待消息或是核心对象被激发。你必须使用一个 MsgWaitForMultipleObjects() 函数。这个函数非常类似WaitForMultipleObjects(),但它会在“对象被激发”或“消息到达队列”时被唤醒而返回。MsgWaitForMultipleObjects() 多接受一个参数,允许指定哪些消息是观察对象。

目录
相关文章
|
12月前
|
安全 Java
【JavaSE专栏76】三态和五态,线程的不同状态:新建、运行、状态、阻塞、等待、计时等待状态
【JavaSE专栏76】三态和五态,线程的不同状态:新建、运行、状态、阻塞、等待、计时等待状态
|
12月前
|
Java 程序员 调度
如何用Java编写代码来等待一个线程join()??
如何用Java编写代码来等待一个线程join()??
35 0
|
2月前
|
API Windows
揭秘网络通信的魔法:Win32多线程技术如何让服务器化身超级英雄,同时与成千上万客户端对话!
【8月更文挑战第16天】在网络编程中,客户/服务器模型让客户端向服务器发送请求并接收响应。Win32 API支持在Windows上构建此类应用。首先要初始化网络环境并通过`socket`函数创建套接字。服务器需绑定地址和端口,使用`bind`和`listen`函数准备接收连接。对每个客户端调用`accept`函数并在新线程中处理。客户端则通过`connect`建立连接,双方可通过`send`和`recv`交换数据。多线程提升服务器处理能力,确保高效响应。
40 6
|
3月前
|
Java C# Python
线程等待(Thread Sleep)
线程等待(Thread Sleep)
|
3月前
|
测试技术
三种等待方式(‌线程等待、‌隐式等待、‌显式等待)
三种等待方式(‌线程等待、‌隐式等待、‌显式等待)
|
4月前
|
安全 Java
使用notifyAll唤醒所有等待线程
使用notifyAll唤醒所有等待线程
|
3月前
|
安全 Java
使用notifyAll唤醒所有等待线程
使用notifyAll唤醒所有等待线程
|
4月前
|
Java
死锁是线程间争夺资源造成的无限等待现象,Java示例展示了两个线程各自持有资源并等待对方释放,导致死锁。`
【6月更文挑战第20天】死锁是线程间争夺资源造成的无限等待现象,Java示例展示了两个线程各自持有资源并等待对方释放,导致死锁。`volatile`保证变量的可见性和部分原子性,确保多线程环境中值的即时更新。与`synchronized`相比,`volatile`作用于单个变量,不保证原子操作,同步范围有限,但开销较小。`synchronized`提供更全面的内存语义,保证原子性和可见性,适用于复杂并发控制。
34 3
|
4月前
|
Java
使用notifyAll唤醒所有等待线程的方法与比较
使用notifyAll唤醒所有等待线程的方法与比较
|
5月前
|
Java API 分布式数据库
实时计算 Flink版产品使用合集之如何解决 TaskManager和 JobManager中有大量的等待线程
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。