Linux系统编程多线程程序如何安全退出

复制代码
/* ============================================================
 *  sm_demo.c
 *  模拟 st_sm_deinit 的三种场景
 *
 *  编译: gcc sm_demo.c -o sm_demo -lpthread
 *  运行:
 *    ./sm_demo 1    worker 已经自己退出,deinit 直接收尾
 *    ./sm_demo 2    worker 优雅退出,deinit 等到后 join
 *    ./sm_demo 3    worker 退不出,deinit 超时后 detach
 * ============================================================ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

/* ---------------- 可调参数 ---------------- */
#define SM_DEINIT_WAIT_MS    1000    /* deinit 最多等 1 秒 */
#define SM_DEINIT_SLICE_MS   10      /* 每 10ms 查一次 */

/* ---------------- 全局状态机 ---------------- */
typedef struct {
    pthread_mutex_t lock;
    pthread_t       worker;
    int             worker_alive;
    int             abort_req;
    int             mode;          /* 1/2/3,决定 worker 行为 */
} sm_t;

static sm_t g_sm;

/* ---------------- worker 线程 ---------------- */
static void *worker_thread(void *arg)
{
    (void)arg;
    printf("[worker] start, mode=%d\n", g_sm.mode);

    pthread_mutex_lock(&g_sm.lock);
    g_sm.worker_alive = 1;
    pthread_mutex_unlock(&g_sm.lock);

    if (g_sm.mode == 1) {
        /* 场景 1:worker 干完活自己退出,不等 abort */
        printf("[worker] mode 1: work 300ms then exit by itself\n");
        usleep(300 * 1000);
    } else if (g_sm.mode == 2) {
        /* 场景 2:worker 检查 abort,收到就退 */
        printf("[worker] mode 2: wait for abort\n");
        while (1) {
            pthread_mutex_lock(&g_sm.lock);
            int stop = g_sm.abort_req;
            pthread_mutex_unlock(&g_sm.lock);
            if (stop) break;
            usleep(10 * 1000);
        }
        printf("[worker] mode 2: got abort, exit\n");
    } else {
        /* 场景 3:worker 忽略 abort,永远不退 */
        printf("[worker] mode 3: ignore abort, never exit\n");
        while (1) {
            usleep(10 * 1000);
        }
    }

    pthread_mutex_lock(&g_sm.lock);
    g_sm.worker_alive = 0;
    pthread_mutex_unlock(&g_sm.lock);

    printf("[worker] exit\n");
    return NULL;
}

/* ---------------- 初始化 ---------------- */
static int sm_init(int mode)
{
    memset(&g_sm, 0, sizeof(g_sm));
    g_sm.mode = mode;
    pthread_mutex_init(&g_sm.lock, NULL);

    if (pthread_create(&g_sm.worker, NULL, worker_thread, NULL) != 0) {
        perror("pthread_create");
        return -1;
    }
    printf("[init] worker created\n");
    return 0;
}

/* ============================================================
 *  sm_deinit ------ 完全照搬 st_sm_deinit 的结构
 * ============================================================ */
static void sm_deinit(void)
{
    int i = 0;
    int alive = 0;
    pthread_t th = 0;

    /* 第 1 段:设 abort_req,读出状态 */
    pthread_mutex_lock(&g_sm.lock);
    g_sm.abort_req = 1;
    alive = g_sm.worker_alive;
    th    = g_sm.worker;
    pthread_mutex_unlock(&g_sm.lock);

    printf("[deinit] alive=%d, th=%lu\n", alive, (unsigned long)th);

    /* 第 2 段:worker 已死,直接收尾 */
    if (!alive) {
        if (th) {
            (void)pthread_join(th, NULL);
            pthread_mutex_lock(&g_sm.lock);
            g_sm.worker = 0;
            pthread_mutex_unlock(&g_sm.lock);
            printf("[deinit] joined dead worker\n");
        }
        pthread_mutex_destroy(&g_sm.lock);
        printf("[deinit] path 2: worker already dead, done\n");
        return;
    }

    /* 第 3 段:轮询等待 */
    printf("[deinit] waiting for worker (max %d ms)...\n", SM_DEINIT_WAIT_MS);
    for (i = 0; i < SM_DEINIT_WAIT_MS / SM_DEINIT_SLICE_MS; i++) {
        pthread_mutex_lock(&g_sm.lock);
        alive = g_sm.worker_alive;
        pthread_mutex_unlock(&g_sm.lock);
        if (!alive) break;
        usleep(SM_DEINIT_SLICE_MS * 1000u);
    }

    /* 第 4 段:等到了,join + 销毁锁 */
    pthread_mutex_lock(&g_sm.lock);
    if (!g_sm.worker_alive && g_sm.worker) {
        th = g_sm.worker;
        g_sm.worker = 0;
        pthread_mutex_unlock(&g_sm.lock);
        (void)pthread_join(th, NULL);
        pthread_mutex_destroy(&g_sm.lock);
        printf("[deinit] path 4: worker exited gracefully, done\n");
        return;
    }

    /* 第 5 段:没等到,detach 兜底 */
    if (g_sm.worker) {
        (void)pthread_detach(g_sm.worker);
        g_sm.worker = 0;
        g_sm.worker_alive = 0;
    }
    pthread_mutex_unlock(&g_sm.lock);
    printf("[deinit] path 5: worker still busy, detached for process exit\n");
}

/* ---------------- main ---------------- */
int main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("usage: %s <1|2|3>\n", argv[0]);
        printf("  1: worker exits by itself before deinit\n");
        printf("  2: worker exits gracefully on abort\n");
        printf("  3: worker never exits, deinit detaches\n");
        return 1;
    }

    int mode = atoi(argv[1]);
    if (mode < 1 || mode > 3) {
        printf("mode must be 1/2/3\n");
        return 1;
    }

    printf("==== mode %d ====\n", mode);
    if (sm_init(mode) != 0) return 1;

    /* 不同场景,等待时间不同 */
    if (mode == 1) {
        /* 等 worker 自己死 */
        sleep(1);
    } else if (mode == 2) {
        /* 等 worker 起来 */
        sleep(1);
    } else {
        /* 等 worker 起来 */
        sleep(1);
    }

    printf("[main] call sm_deinit\n");
    sm_deinit();

    printf("[main] done\n");
    return 0;
}

代码里面的知识点:

一张图总结"为什么"

复制代码
进程要退出,worker 可能还在跑
        │
        ▼
不能直接杀(丢数据、坏状态)
        │
        ▼
优雅退出:通知 + 等待 + 收尾
        │
        ├─ 通知 → abort_req
        ├─ 知状态 → worker_alive(加锁保护)
        ├─ 等待 → 轮询 + 超时
        ├─ 等到了 → join + 销毁锁
        └─ 等不到 → detach 兜底,不销毁锁
相关推荐
Fcy6481 小时前
Linux下 数据链路层详解(从以太网到ARP协议)
linux·mac·以太网·数据链路层·arp
‎ദ്ദിᵔ.˛.ᵔ₎3 小时前
linux 进程信号(下)
linux·运维·服务器
skywalk81633 小时前
在FreeBSD系统的linux仿真环境下安装和使用kiro 这个亚马逊的AI agent
linux·人工智能·freebsd·kiro
Mikko73 小时前
Linux 磁盘满了但 du 找不到?LVM 扩容 df 不变?fstab 写错开不了机?三台真机实测磁盘排查
linux·运维·centos
Madison-No74 小时前
多语言聊天大模型--测试报告
linux·git·python·selenium·jmeter·自动化·postman
Liuqy-054 小时前
特殊进程——孤儿、僵尸、守护进程
linux·运维·服务器
流浪0014 小时前
Linux系统篇47——线程(十二) POSIX 信号量与环形队列,把判空判满提前到访问之前
linux·操作系统·线程·信号量·环形队列
咖丨喱4 小时前
【MMC Core + hc16 Host(WiFi SDIO 如何落到控制器)】
linux
等我调个Bug4 小时前
【Linux系统篇】权限
linux·服务器