当前位置:首页 > 技术 > 正文内容

基于POSIX标准的多线程同步机制与线程池实现

访客 技术 2026年7月20日 3

在Linux环境下,使用POSIX线程(pthread)库可以高效地实现多线程程序中的同步与通信。本文将介绍互斥锁的不同类型、条件变量和信号量的使用方式,并最终构建一个可复用的线程池框架。

互斥锁属性配置

互斥锁可通过属性对象设置其行为特性。以下代码展示了如何根据命令行参数设定互斥锁的类型:

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

int main(int argc, char *argv[]) {
    pthread_mutex_t mutex;
    pthread_mutexattr_t attr;

    if (argc != 2) {
        fprintf(stderr, "请指定锁类型: normal, errorcheck, 或 recursive\n");
        return EXIT_FAILURE;
    }

    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);

    if (strcmp(argv[1], "errorcheck") == 0) {
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
    } else if (strcmp(argv[1], "recursive") == 0) {
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    } // 默认为 NORMAL 类型

    pthread_mutex_init(&mutex, &attr);

    // 尝试重复加锁
    pthread_mutex_lock(&mutex);
    printf("首次加锁成功\n");

    int result = pthread_mutex_lock(&mutex);
    if (result != 0) {
        printf("二次加锁失败,错误码: %d\n", result);
    } else {
        printf("二次加锁成功\n");
        pthread_mutex_unlock(&mutex);
    }

    pthread_mutex_unlock(&mutex);
    pthread_mutex_destroy(&mutex);
    pthread_mutexattr_destroy(&attr);

    return 0;
}

使用条件变量实现线程协作

条件变量常用于线程间的通知机制。下面示例中,主线程读取输入后唤醒工作线程处理数据:

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

#define BUFFER_SIZE 32

char shared_buffer[BUFFER_SIZE];
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int data_ready = 0;

void* input_thread(void* arg) {
    fgets(shared_buffer, BUFFER_SIZE, stdin);
    size_t len = strlen(shared_buffer);
    if (len > 0 && shared_buffer[len-1] == '\n') {
        shared_buffer[len-1] = '\0';
    }
    
    pthread_mutex_lock(&mtx);
    data_ready = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mtx);

    pthread_exit(NULL);
}

void* process_thread(void* arg) {
    pthread_mutex_lock(&mtx);
    while (!data_ready) {
        pthread_cond_wait(&cond, &mtx);
    }
    printf("接收到数据: %s\n", shared_buffer);
    pthread_mutex_unlock(&mtx);
    pthread_exit(NULL);
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, input_thread, NULL);
    pthread_create(&t2, NULL, process_thread, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return 0;
}

线程池设计与实现

线程池通过预创建线程减少频繁创建销毁的开销。核心结构包含任务队列、互斥锁和条件变量:

// threadpool.h
#ifndef THREAD_POOL_H
#define THREAD_POOL_H

typedef struct Task {
    void (*function)(void*);
    void* argument;
    struct Task* next;
} Task;

typedef struct ThreadPool {
    pthread_mutex_t lock;
    pthread_cond_t notify;
    Task* first;
    int thread_count;
    int task_count;
    int shutdown;
    pthread_t* threads;
} ThreadPool;

void threadpool_init(ThreadPool* pool, int num_threads);
int threadpool_add_task(ThreadPool* pool, void (*func)(void*), void* arg);
int threadpool_destroy(ThreadPool* pool);

#endif

工作线程循环等待新任务:

static void* worker_routine(void* arg) {
    ThreadPool* pool = (ThreadPool*)arg;
    Task* current;

    while (1) {
        pthread_mutex_lock(&pool->lock);

        while (pool->task_count == 0 && !pool->shutdown) {
            pthread_cond_wait(&pool->notify, &pool->lock);
        }

        if (pool->shutdown) break;

        current = pool->first;
        if (current) {
            pool->first = current->next;
            pool->task_count--;
        }

        pthread_mutex_unlock(&pool->lock);

        if (current) {
            current->function(current->argument);
            free(current);
        }
    }

    pthread_mutex_unlock(&pool->lock);
    pthread_exit(NULL);
    return NULL;
}

主函数中初始化线程池除并提交多个任务:

void sample_task(void* param) {
    int id = *(int*)param;
    printf("执行任务 #%d in thread 0x%lx\n", id, pthread_self());
    sleep(1);
}

int main() {
    ThreadPool pool;
    threadpool_init(&pool, 3);

    for (int i = 0; i < 10; i++) {
        int* tid = malloc(sizeof(int));
        *tid = i;
        threadpool_add_task(&pool, sample_task, tid);
    }

    sleep(5);
    threadpool_destroy(&pool);
    return 0;
}

编译与构建

使用Makefile管理项目依赖:

.PHONY: all clean
TARGET = app
OBJS = main.o threadpool.o

all: $(TARGET)

$(TARGET): $(OBJS)
	gcc -o $@ $^ -lpthread

%.o: %.c
	gcc -c -Wall $< -o $@

clean:
	rm -f *.o $(TARGET)
标签: pthreadmutex

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。