首页 物联网

Linux 多线程并发编程:原理、封装与避坑指南

分类:物联网
字数: (2125)
阅读: (4946)
内容摘要:Linux 多线程并发编程:原理、封装与避坑指南,

在高性能后端服务开发中,Linux 多线程技术扮演着至关重要的角色。例如,在高并发的 Nginx 服务器中,worker 进程通常会创建多个线程来处理客户端请求,充分利用多核 CPU 的计算能力。如果不理解多线程的底层原理,以及如何进行有效的封装,很容易导致程序出现死锁、竞争条件等问题,从而影响系统的稳定性和性能。本文将深入探讨 Linux 多线程的创建、封装以及实战中的注意事项,助你写出更健壮、更高效的多线程程序。

线程创建的底层原理

Linux 平台上创建线程主要依赖 pthread 库。pthread_create 函数是创建线程的核心,它接收线程属性、线程函数指针以及传递给线程函数的参数。理解 pthread_create 的工作机制,是掌握多线程编程的基础。

Linux 多线程并发编程:原理、封装与避坑指南
#include <pthread.h>
#include <stdio.h>

void *thread_function(void *arg) {
    int thread_id = *(int *)arg;
    printf("线程 %d 正在执行\n", thread_id);
    pthread_exit(NULL); // 线程退出
}

int main() {
    pthread_t thread1, thread2; // 线程 ID
    int id1 = 1, id2 = 2;
    int ret1, ret2;

    ret1 = pthread_create(&thread1, NULL, thread_function, &id1); // 创建线程 1
    if (ret1) {
        perror("pthread_create error");
        return 1;
    }

    ret2 = pthread_create(&thread2, NULL, thread_function, &id2); // 创建线程 2
    if (ret2) {
        perror("pthread_create error");
        return 1;
    }

    pthread_join(thread1, NULL); // 等待线程 1 结束
    pthread_join(thread2, NULL); // 等待线程 2 结束

    printf("所有线程执行完毕\n");
    return 0;
}

pthread_create 的第一个参数是指向 pthread_t 类型的指针,用于存储新创建线程的 ID。第二个参数是线程属性,通常设置为 NULL 使用默认属性。第三个参数是线程函数,也就是线程要执行的代码。第四个参数是传递给线程函数的参数。pthread_join 函数用于等待指定的线程结束,防止主线程提前退出,导致子线程被强制终止。

Linux 多线程并发编程:原理、封装与避坑指南

多线程封装的最佳实践

直接使用 pthread 库进行多线程编程,代码可读性和可维护性较差。因此,我们需要对多线程进行封装,提供更简洁易用的接口。一种常见的封装方式是创建一个线程池,线程池可以预先创建一批线程,并维护一个任务队列。当有新的任务到达时,从线程池中选择一个空闲线程来执行任务。这种方式可以避免频繁创建和销毁线程的开销,提高系统的性能。

Linux 多线程并发编程:原理、封装与避坑指南
#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>

class ThreadPool {
public:
    ThreadPool(int num_threads) : num_threads_(num_threads), running_(true) {
        threads_.resize(num_threads_);
        for (int i = 0; i < num_threads_; ++i) {
            pthread_create(&threads_[i], NULL, worker_thread, this); // 创建工作线程
        }
    }

    ~ThreadPool() {
        running_ = false;
        pthread_cond_broadcast(&condition_); // 唤醒所有线程
        for (int i = 0; i < num_threads_; ++i) {
            pthread_join(threads_[i], NULL); // 等待所有线程结束
        }
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&condition_);
    }

    void enqueue(void (*function)(void*), void* arg) {
        pthread_mutex_lock(&mutex_);
        tasks_.push(std::make_pair(function, arg)); // 将任务添加到队列
        pthread_cond_signal(&condition_); // 唤醒一个线程
        pthread_mutex_unlock(&mutex_);
    }

private:
    int num_threads_;
    std::vector<pthread_t> threads_;
    std::queue<std::pair<void (*)(void*), void*>> tasks_;
    pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t condition_ = PTHREAD_COND_INITIALIZER;
    bool running_;

    static void* worker_thread(void* arg) {
        ThreadPool* pool = static_cast<ThreadPool*>(arg);
        while (pool->running_) {
            pthread_mutex_lock(&pool->mutex_);
            while (pool->tasks_.empty() && pool->running_) {
                pthread_cond_wait(&pool->condition_, &pool->mutex_); // 等待任务
            }

            if (!pool->running_) {
                pthread_mutex_unlock(&pool->mutex_);
                pthread_exit(NULL);
            }

            std::pair<void (*)(void*), void*> task = pool->tasks_.front();
            pool->tasks_.pop();
            pthread_mutex_unlock(&pool->mutex_);

            task.first(task.second); // 执行任务
        }
        pthread_exit(NULL);
    }
};

void my_task(void* arg) {
    int task_id = *(int*)arg;
    std::cout << "执行任务: " << task_id << " 线程ID: " << pthread_self() << std::endl;
    sleep(1);
}

int main() {
    ThreadPool pool(4); // 创建一个包含 4 个线程的线程池
    for (int i = 0; i < 10; ++i) {
        int* arg = new int(i); // 创建任务参数
        pool.enqueue(my_task, arg); // 将任务添加到线程池
    }
    sleep(5);
    return 0;
}

上述代码实现了一个简单的线程池。ThreadPool 类负责管理线程的创建、销毁以及任务的调度。enqueue 方法用于将任务添加到任务队列中。worker_thread 是线程函数,它会不断从任务队列中取出任务并执行。需要注意的是,在多线程环境下,对共享资源(如任务队列)的访问需要进行同步,以避免竞争条件。代码中使用了互斥锁(pthread_mutex_t)和条件变量(pthread_cond_t)来实现线程同步。

Linux 多线程并发编程:原理、封装与避坑指南

实战避坑经验总结

  1. 避免死锁:死锁是指多个线程互相等待对方释放资源,导致所有线程都无法继续执行的情况。要避免死锁,需要保证线程以相同的顺序获取锁,并且尽量减少持有锁的时间。
  2. 谨慎使用共享变量:多个线程同时访问和修改共享变量时,可能会导致数据不一致。可以使用互斥锁、读写锁等同步机制来保护共享变量。
  3. 注意内存管理:在多线程环境下,内存管理更加复杂。需要避免内存泄漏和野指针等问题。可以使用智能指针等技术来简化内存管理。
  4. 选择合适的线程数量:线程数量并非越多越好。过多的线程会导致上下文切换的开销增加,反而降低系统的性能。需要根据实际情况选择合适的线程数量。通常情况下,线程数量等于 CPU 核心数是一个不错的选择。
  5. 合理利用锁的粒度:锁的粒度越细,并发度越高,但同时也增加了锁管理的开销。需要根据实际情况选择合适的锁粒度。例如,如果只需要保护一个小的共享变量,可以使用自旋锁;如果需要保护一个大的数据结构,可以使用读写锁。

在实际应用中,例如使用宝塔面板部署的 Web 应用,通过 Nginx 反向代理可以有效分发请求到后端的多线程服务,提高应用的并发连接数。理解和掌握 Linux 多线程的创建和封装,并结合具体的业务场景进行优化,才能真正发挥多线程的优势,构建高性能的后端系统。

Linux 多线程并发编程:原理、封装与避坑指南

转载请注明出处: 加班到秃头

本文的链接地址: http://m.acea1.store/blog/531439.SHTML

本文最后 发布于2026-04-08 03:14:04,已经过了19天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 躺平青年 4 天前
    感谢分享,正在学习 Linux 多线程编程,这篇文章帮我理清了思路。