You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows C中如何基于主线程条件正确关闭/退出子线程?

问题分析

你的代码存在几个关键问题导致子线程无法被正确终止:

  • 子线程的while(1)死循环未检查ts.running状态,主线程修改标志后子线程完全无响应
  • CloseHandle(volume_h_thread)仅关闭线程句柄,不会终止线程执行;ExitThread是线程自我终止的函数,不能用来终止其他线程
  • 子线程里的Sleep(SLEEP_TIME)是阻塞调用,就算状态改变,也要等Sleep结束才会检查,无法及时响应启停命令
修正方案

我们用事件对象配合状态标志实现线程的优雅启停,让子线程能及时响应终止信号:

  1. 创建手动重置事件,用于通知子线程退出
  2. 子线程用WaitForSingleObject替代Sleep,既实现定时,又能被事件中断
  3. 主线程通过触发事件通知子线程终止,等待线程退出后再重新创建新线程
修正后的完整代码
#include <stdio.h>
#include <windows.h>
#define SLEEP_TIME 5000 // 5 seconds

// 全局事件:用于通知子线程退出
HANDLE hExitEvent = NULL;
// 线程运行状态标志
BOOL g_threadRunning = TRUE;

DWORD WINAPI volume_loop(void *data)
{
    unsigned int times_pressed = 0;
    INPUT ip;
    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0;
    ip.ki.time = 0;
    ip.ki.dwExtraInfo = 0;

    while (TRUE)
    {
        // 等待SLEEP_TIME,期间收到退出信号会立即返回
        DWORD waitResult = WaitForSingleObject(hExitEvent, SLEEP_TIME);
        if (waitResult == WAIT_OBJECT_0)
        {
            // 收到退出信号,终止线程
            printf("Volume thread exiting...\n");
            break;
        }
        else if (waitResult == WAIT_TIMEOUT)
        {
            // 时间到,执行音量切换操作
            times_pressed++;
            printf("Pressing volume up/down for the %u time!\n", times_pressed);

            // 按下音量+
            ip.ki.wVk = VK_VOLUME_UP;
            ip.ki.dwFlags = 0;
            SendInput(1, &ip, sizeof(INPUT));
            // 释放音量+
            ip.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &ip, sizeof(INPUT));

            Sleep(250);

            // 按下音量-
            ip.ki.wVk = VK_VOLUME_DOWN;
            ip.ki.dwFlags = 0;
            SendInput(1, &ip, sizeof(INPUT));
            // 释放音量-
            ip.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &ip, sizeof(INPUT));
        }
    }
    return 0;
}

int main()
{
    // 创建手动重置的退出事件,初始为未触发状态
    hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hExitEvent == NULL)
    {
        printf("CreateEvent failed! Error: %d\n", GetLastError());
        return 1;
    }

    HANDLE volume_h_thread = CreateThread(NULL, 0, volume_loop, NULL, 0, NULL);
    if (volume_h_thread == NULL)
    {
        printf("CreateThread failed! Error: %d\n", GetLastError());
        CloseHandle(hExitEvent);
        return 1;
    }

    while (TRUE)
    {
        Sleep(10000); // 每10秒切换一次状态
        if (g_threadRunning)
        {
            puts("Turning thread off!");
            // 触发退出事件,通知子线程终止
            SetEvent(hExitEvent);
            // 等待子线程完全退出
            WaitForSingleObject(volume_h_thread, INFINITE);
            // 关闭线程句柄
            CloseHandle(volume_h_thread);
            g_threadRunning = FALSE;
            // 重置事件,为下次启动做准备
            ResetEvent(hExitEvent);
        }
        else
        {
            puts("Turning thread on!");
            // 创建新的子线程
            volume_h_thread = CreateThread(NULL, 0, volume_loop, NULL, 0, NULL);
            if (volume_h_thread == NULL)
            {
                printf("CreateThread failed! Error: %d\n", GetLastError());
                CloseHandle(hExitEvent);
                return 1;
            }
            g_threadRunning = TRUE;
        }
    }

    // 清理资源(实际不会执行到这里,因为是死循环)
    SetEvent(hExitEvent);
    WaitForSingleObject(volume_h_thread, INFINITE);
    CloseHandle(volume_h_thread);
    CloseHandle(hExitEvent);
    return 0;
}
关键修改说明
  • 事件对象替代全局标志:用CreateEvent创建手动重置事件,主线程通过SetEvent触发退出信号,子线程用WaitForSingleObject等待,兼顾定时与即时响应终止命令的需求
  • 优雅终止线程:子线程收到退出信号后自行退出,避免强制终止线程带来的资源泄漏问题
  • 等待线程退出:主线程在终止线程后调用WaitForSingleObject确保子线程完全退出,再关闭句柄,避免资源泄漏
  • 重置事件:每次启动新线程前重置事件,保证新线程能正常等待定时信号

内容的提问来源于stack exchange,提问作者Coldchain9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 10:22:42