linux多线程编程(四)

Linux多线程编程(四)攻略

前言

本文将讲解在Linux环境下进行多线程编程的基本概念、操作方法和注意事项,通过示例代码演示实现多线程的一些常见用法。

基础知识

线程的创建和销毁

线程是轻量级的进程,一个进程可以包含多个线程。线程的创建和销毁都是通过pthread库中的函数来完成的:

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

int pthread_join(pthread_t thread, void **retval);

int pthread_cancel(pthread_t thread);
  • pthread_create函数用于创建一个线程,并将它开始执行的起始点指定为start_routine(这个起始点函数必须是void* func(void*)形式的函数指针)。该函数会返回一个线程ID,存放在thread中,可以通过该线程ID对线程进行操作。
  • pthread_join函数用于等待一个线程结束,并获取它的返回值(如果有的话)。如果线程在执行pthread_exit或返回语句时传入了一个指针,那么这个指针就作为retval的值传递回来。
  • pthread_cancel函数用于向线程发送一个取消信号,线程可以在任意时刻响应这个信号并结束运行。

线程的同步

多个线程在共享同一个资源时会出现竞态问题,这时需要使用线程同步机制来保证数据的正确性。常用的线程同步机制有:

  • 互斥锁:即mutex,提供对共享资源的互斥访问,防止多个线程同时读写资源。
  • 条件变量:即cond,用于线程之间的通信和唤醒。
  • 信号量:即sem,用于线程之间的通信和控制并发。

线程的并发

在多线程编程中,线程的并发性是非常重要的,可以通过以下几种方式来提高线程的并发性:

  • 减小锁的粒度
  • 使用无锁的数据结构
  • 增加CPU核心数
  • 使用协程等更高级别的并发方式

实战

示例一:使用线程池进行并发处理

下面的示例是使用线程池对多个任务进行并发处理的代码。

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

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

typedef struct {
    job_t *head;
    job_t *tail;
    int job_count;
    int thread_count;
    pthread_mutex_t mutex;
    pthread_cond_t condition;
} threadpool_t;

threadpool_t *threadpool_create(int thread_count)
{
    threadpool_t *pool = (threadpool_t *)malloc(sizeof(threadpool_t));
    pthread_mutex_init(&pool->mutex, NULL);
    pthread_cond_init(&pool->condition, NULL);
    pool->head = NULL;
    pool->tail = NULL;
    pool->job_count = 0;
    pool->thread_count = thread_count;
    for (int i = 0; i < thread_count; i++) {
        pthread_t worker;
        pthread_create(&worker, NULL, threadpool_worker, pool);
        pthread_detach(worker);
    }
    return pool;
}

int threadpool_add_job(threadpool_t *pool, void *(*function)(void *), void *argument)
{
    job_t *new_job = (job_t *)malloc(sizeof(job_t));
    new_job->function = function;
    new_job->argument = argument;
    new_job->next = NULL;
    pthread_mutex_lock(&pool->mutex);
    if (pool->head == NULL) {
        pool->head = new_job;
        pool->tail = new_job;
    } else {
        pool->tail->next = new_job;
        pool->tail = new_job;
    }
    pool->job_count++;
    pthread_mutex_unlock(&pool->mutex);
    pthread_cond_signal(&pool->condition);
    return 0;
}

void *threadpool_worker(void *arg)
{
    threadpool_t *pool = (threadpool_t *)arg;
    while (1) {
        pthread_mutex_lock(&pool->mutex);
        while (pool->head == NULL)
            pthread_cond_wait(&pool->condition, &pool->mutex);
        job_t *job = pool->head;
        pool->head = pool->head->next;
        if (pool->head == NULL)
            pool->tail = NULL;
        pool->job_count--;
        pthread_mutex_unlock(&pool->mutex);
        job->function(job->argument);
        free(job);
    }
}

void threadpool_destroy(threadpool_t *pool)
{
    pthread_mutex_lock(&pool->mutex);
    job_t *current = pool->head;
    while (current != NULL) {
        job_t *next = current->next;
        free(current);
        current = next;
    }
    pthread_mutex_unlock(&pool->mutex);
    pthread_mutex_destroy(&pool->mutex);
    pthread_cond_destroy(&pool->condition);
    free(pool);
}

void *task(void *arg)
{
    int n = *(int *)arg;
    printf("Task %d is running\n", n);
    sleep(1);
    printf("Task %d is done\n", n);
    return NULL;
}

int main()
{
    threadpool_t *pool = threadpool_create(4);
    int n = 10;
    for (int i = 0; i < n; i++)
        threadpool_add_job(pool, task, &i);
    sleep(10);
    threadpool_destroy(pool);
    return 0;
}

该代码实现了一个简单的线程池,可以对多个任务进行并发处理。

示例二:使用互斥锁保护共享资源

下面的示例是使用互斥锁保护共享资源的代码。

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

#define THREAD_COUNT 10

int counter = 0;
pthread_mutex_t mutex;

void *increment(void *arg)
{
    for (int i = 0; i < 100000; i++) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main()
{
    pthread_t threads[THREAD_COUNT];
    pthread_mutex_init(&mutex, NULL);
    for (int i = 0; i < THREAD_COUNT; i++)
        pthread_create(&threads[i], NULL, increment, NULL);
    for (int i = 0; i < THREAD_COUNT; i++)
        pthread_join(threads[i], NULL);
    printf("Counter = %d\n", counter);
    pthread_mutex_destroy(&mutex);
    return 0;
}

该代码使用互斥锁保护了一个共享变量,避免了多个线程同时读写该变量的产生竞态问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:linux多线程编程(四) - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C++实现考勤管理系统

    C++实现考勤管理系统 简介 考勤管理系统是一种可以对员工的出勤情况进行统计和分析的工具,它可以帮助企业更加有效地管理其员工,并且提高员工出勤率。本文将介绍如何用C++实现考勤管理系统。 设计思路 考勤管理系统需要满足以下功能: 输入员工的考勤信息。 对员工的考勤记录进行存储和管理。 统计员工的出勤情况,并给出相应的报告。 为了实现这些功能,我们需要设计以下…

    C 2023年5月23日
    00
  • PHP局部异常因子算法-Local Outlier Factor(LOF)算法的具体实现解析

    PHP局部异常因子算法-Local Outlier Factor(LOF)算法的具体实现解析 什么是Local Outlier Factor(LOF)算法 Local Outlier Factor,即局部异常因子算法,是一种用于检测数据集中的异常值的非监督学习算法。它可以发现在数据集中位置比较突出且与其相邻数据点比较远的点。 LOF算法可以对离散数据集进行处…

    C 2023年5月22日
    00
  • C# JsonHelper 操作辅助类,拿来直接用

    首先,为了更好地讲解这个“C# JsonHelper操作辅助类,拿来直接用”的攻略,我们需要了解以下几个重要概念: C#: 一种面向对象的、现代的、通用的、类型安全的编程语言,由微软公司开发并推广,被广泛运用于开发各种类型的应用程序。 Json: 一种轻量级的数据交换格式,基于JavaScript语言的语法规则,易于人们阅读和编写,同时也易于机器解析和生成,…

    C 2023年5月23日
    00
  • 一次因信号量引发的tomcat异常退出解决

    下面是一次因信号量引发的Tomcat异常退出解决的完整攻略: 背景 在使用Tomcat时,有时候可能会因为进程无法获取到信号量而导致Tomcat异常退出。这种问题通常会在并发量较大的情况下出现。 解决方法 解决这种问题的方法是通过增加操作系统的信号量来提高并发量。下面是具体的操作步骤: 查看当前信号量的情况: ipcs -ls 在这个命令中,参数 -l 表示…

    C 2023年5月22日
    00
  • C++实现读写文件的示例代码

    下面是关于C++实现读写文件的示例代码的攻略。 一、前置知识 在开始写C++读写文件的代码之前,你需要有一些基本的前置知识: 文件指针(FILE*):表示文件句柄,用于打开、关闭文件,以及进行读、写、定位等操作。 文件操作模式:用于指定打开文件的模式,例如读取、写入、追加等。 文件读写函数:主要有fscanf、fprintf、fgets、fputs、frea…

    C 2023年5月24日
    00
  • C++为什么不能修改set里的值?非要修改怎么办?

    C++为什么不能修改set里的值 set是C++ STL库中的一个容器,它使用平衡二叉搜索树作为实现机制。这种数据结构会在插入或删除元素时维护树的平衡,从而使得查找等操作的时间复杂度保持在O(log n)级别。而且,set自身所提供的插入、删除和查找操作也能保证元素的唯一性,因此适用于需要去重的情况。 set中元素的顺序是按照元素的大小由小到大排列的,在该容…

    C 2023年5月23日
    00
  • 一文详解JavaScript数组对象和字符串对象

    一文详解JavaScript数组对象和字符串对象 简介 本文将详细介绍JavaScript中的数组对象和字符串对象,并给出一些示例说明。 数组对象 定义和初始化 在JavaScript中,数组是一个有序的集合,可以通过下标来访问或修改其中的元素。 初始化一个空数组: let arr1 = []; 初始化一个带有初始元素的数组: let arr2 = [1, …

    C 2023年5月23日
    00
  • 操作系统中的Hosts文件工作原理和作用及其详细介绍

    操作系统中的Hosts文件工作原理和作用及其详细介绍 Hosts文件介绍 在计算机网络中,Hosts文件是一个用于存储 IP 地址和主机名(域名)对应关系的纯文本文件,通常位于操作系统的系统目录下,在 Windows 系统中为 C:\Windows\System32\drivers\etc\hosts 文件。该文件是本地DNS的重要组成部分,可以将特定的主机…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部