Linux线程

Linux线程

前言

  • 一个程序内的多个执行路线
  • 一个进程内部的控制序列
  • 新的线程拥有自己的栈,与它的创建者共享全局变量,文件描述符,信号句柄等资源
  • 线程执行开销小,但不利于资源的管理和保护

线程函数

pthread_create函数

  • 创建一个新的线程
接口代码
#include <pthread.h>

int pthread_create(pthread_t* thread,pthread_attr_t* attr,void*(*start_routine)(void *),void* arg);
参数解释
  • thread: 线程标识符
  • attr: 用于指定线程属性,可以使用NULL表示使用默认属性.常用线程属性包括堆栈大小,调度策略
  • start_routine: 线程启动函数指针,就是线程启动函数
  • arg: 线程启动函数参数
返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码,并且thread不会被修改。常见的错误码包括:
    • EAGAIN:系统资源不足,无法创建新的线程。
    • EINVALattr 指向的属性无效。
    • ENOMEM:内存不足,无法分配所需的资源。
实例代码
// 见下方汇总

pthread_exit

  • 线程终止函数
  • 终止线程并不会释放互斥锁
接口代码
#include <pthread.h>

void pthread_exit(void* retval);
参数解释
  • retval: 返回值,可以为NULL
实例代码
// 见下方汇总

pthread_join

  • 线程等待函数,类似于wait函数
接口代码
#include <pthread.h>

int pthread_join(pthread_t th,void** thread_return);
参数解释
  • th: 线程描述符
  • thread_return: 所等待的线程的返回值
返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码。常见的错误码包括:
    • ESRCH:指定的线程 ID 不存在。
    • EINVAL:线程 ID 无效。
    • EDEADLK:检测到死锁情况,通常是因为尝试对已经加入或分离的线程再次调用 pthread_join
实例代码
// 见下方汇总

练习–线程创建,关闭

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

// 线程启动函数
void *thread_function(void *arg)
{
    // 接受参数
    int *val = (int *)arg;
    printf("线程ID: %lu,参数: %d\n", pthread_self(), *val);
    *val = 999;
    // 线程退出
    pthread_exit((void *)(__intptr_t)*val);
    //  pthread_exit(NULL)
    // return (void *)(__intptr_t)*val;
}

int main()
{
    pthread_t thread; // 线程标识符
    int val = 100;    // 传递给线程的参数

    // 创建新线程
    if (pthread_create(&thread, NULL, thread_function, &val) != 0)
    {
        perror("线程创建失败");
        exit(1);
    }

    // 主线程继续执行
    printf("主线程ID: %lu\n", pthread_self());

    // 等待线程结束
    void *threadRes;
    if (pthread_join(thread, &threadRes) != 0)
    {
        perror("线程异常");
        exit(1);
    }

    // 打印线程返回值
    if (threadRes != NULL)
    {
        int res = (__intptr_t)threadRes;
        printf("线程返回值为: %d\n", res);
    }
    else
        printf("线程没有返回值\n");
    exit(0);
}

线程同步机制

  • 信号量: 仅用于线程间同步
  • 互斥量: 信号量的另一种应用,线程同步机制的一种,某个进程先获得资源后,后访问资源的线程会被阻塞

信号量

sem_init
  • 信号量初始化
接口代码
#include <semaphore.h>

int sem_init(sem_t* sem,int pshared,unsigned int value);
参数解释
  1. sem:
    • 类型:sem_t *
    • 描述:这是一个指向 sem_t 类型的指针,表示要初始化的信号量对象。
  2. pshared:
    • 类型:int
    • 描述:这个参数指定信号量的共享范围。
      • 如果 pshared 为 0,信号量将在单个进程内共享。
      • 如果 pshared 不为 0,信号量可以在多个进程之间共享。在这种情况下,sem 指向的内存必须位于共享内存段中(例如通过 shm_openmmap 创建的共享内存)。
  3. value:
    • 类型:unsigned int
    • 描述:这是信号量的初始值。信号量的值通常表示可用资源的数量。初始值可以是任何非负整数。
返回值
  • 成功:返回 0。
  • 失败:返回 -1,并设置errno以指示错误类型。常见的错误码包括:
    • EINVALvalue 超出了信号量的最大值。
    • EAGAIN:系统资源不足,无法初始化信号量。
sem_wait/sem_post
  • sem_wait: 信号量减一,当信号量减为0时,就阻塞线程
  • sem_post: 信号量加一
接口代码
#include <semaphore.h>

int sem_wait(sem_t* sem);
int sem_post(sem_t* sem);
参数解释
  • sem: 信号量
返回值
  • 成功:返回0
  • 不成功:返回-1
sem_destory
  • 信号量清理函数sem_destory
接口代码
#include <semaphore.h>

int sem_destory(sen_t* sem);
参数解释
  • sem: 信号量
返回值
  • 成功:返回0
  • 不成功:返回-1

练习–信号量访问临界区资源

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

// 信号量
sem_t sem;

// 共享资源
int shared_num = 0;

// 线程启动函数
void *thread(void *arg)
{
    int thread_id = *(int *)arg;

    // 等待信号量
    if (sem_wait(&sem) == -1)
    {
        perror("临界区正在被访问");
        pthread_exit(NULL);
    }

    // 进入临界区
    printf("线程 ID: %d, 进入临界区\n", thread_id);

    // 模拟睡眠
    sleep(2);

    // 访问共享资源
    shared_num++;
    printf("线程 ID: %d, 修改共享资源: %d\n", thread_id, shared_num);

    // 退出临界区
    printf("线程 ID: %d, 退出临界区\n", thread_id);

    // 释放信号量
    if (sem_post(&sem) == -1)
    {
        perror("释放失败");
        pthread_exit(NULL);
    }

    return NULL;
}

int main()
{
    pthread_t thread1, thread2;
    int id1 = 1, id2 = 2;

    // 初始化信号量
    if (sem_init(&sem, 0, 1) == -1)
    {
        perror("信号量初始化失败");
        exit(-1);
    }

    // 创建线程
    if (pthread_create(&thread1, NULL, thread, &id1) == -1)
    {
        perror("线程一创建失败");
        exit(1);
    }

    if (pthread_create(&thread2, NULL, thread, &id2) == -1)
    {
        perror("线程二创建失败");
        exit(1);
    }

    // 主线程继续执行
    printf("主线程 ID: %lu\n", pthread_self());

    // 等待线程结束
    if (pthread_join(thread1, NULL) == -1)
    {
        perror("线程一异常");
        exit(1);
    }
    if (pthread_join(thread2, NULL) == -1)
    {
        perror("线程二失败");
        exit(1);
    }

    // 销毁信号量
    if (sem_destroy(&sem) != 0)
    {
        perror("信号量销毁失败");
        exit(1);
    }

    exit(0);
}

互斥锁

  • 基于信号量,但比信号量要求更严格,同一时间只允许一个进程访问临界区资源
pthread_mutex_init
  • 初始化互斥量锁
接口代码
#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t *mutexattr);
参数解释
  • mutex: 互斥锁对象
  • mutexattr: 指定互斥锁属性,默认的话写NULL
    • 类型:互斥锁的类型,例如普通互斥锁、递归互斥锁等。
    • 协议:互斥锁的优先级继承或优先级天花板协议。
    • 进程共享:互斥锁是否可以在多个进程之间共享。
返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码。常见的错误码包括:
    • EINVALmutexmutexattr 指向无效的地址。
    • ENOMEM:系统资源不足,无法初始化互斥锁。
pthread_mutex_lock\pthread_mutex_unlock
  • pthread_mutex_lock: 加锁,别的进程阻塞
  • pthread_mutex_unlock: 解锁
接口代码
#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数解释
  • mutex: 互斥锁对象
返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码。
pthread_mutex_destory
  • 清理互斥锁
接口代码
#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数解释
  • mutex: 互斥锁对象
返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码。

练习–互斥访问临界区资源

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
// 定义互斥锁
pthread_mutex_t mutex;

// 共享资源
int shareSource = 0;

// 线程启动函数
void *thread(void *arg)
{
    int threadID = *(int *)arg;

    // 加锁
    if (pthread_mutex_lock(&mutex) == -1)
    {
        perror("临界区正在使用");
        pthread_exit(NULL);
    }

    // 进入临界区
    printf("线程 ID: %d, 进入临界区\n", threadID);

    // 模拟一些工作
    sleep(2);

    // 访问共享资源
    shareSource++;
    printf("线程 ID: %d, 修改共享资源: %d\n", threadID, shareSource);

    // 退出临界区
    printf("线程 ID: %d, 退出临界区\n", threadID);

    // 解锁
    if (pthread_mutex_unlock(&mutex) == -1)
    {
        perror("解锁失败");
        pthread_exit(NULL);
    }

    return NULL;
}

int main()
{
    pthread_t thread1, thread2;
    int id1 = 1, id2 = 2;

    // 初始化互斥锁
    if (pthread_mutex_init(&mutex, NULL) == -1)
    {
        perror("初始化互斥锁失败");
        exit(1);
    }

    // 创建线程
    if (pthread_create(&thread1, NULL, thread, &id1) == -1)
    {
        perror("线程一创建失败");
        exit(1);
    }

    if (pthread_create(&thread2, NULL, thread, &id2) == -1)
    {
        perror("线程二创建失败");
        exit(1);
    }

    // 主线程继续执行
    printf("主线程 ID: %lu\n", pthread_self());

    // 等待线程结束
    if (pthread_join(thread1, NULL) == -1)
    {
        perror("线程一异常");
        exit(1);
    }
    if (pthread_join(thread2, NULL) == -1)
    {
        perror("线程二失败");
        exit(1);
    }

    // 销毁互斥锁
    if (pthread_mutex_destroy(&mutex) == -1)
    {
        perror("互斥锁销毁失败");
        exit(1);
    }
    exit(0);
}

线程控制

  • 可以独自设置线程的配置,例如
    • 堆栈大小:设置线程的堆栈大小。
    • 堆栈地址:指定线程堆栈的起始地址。
    • 分离状态:指定线程是否是分离的(detached)。分离的线程在终止时会自动释放资源,不需要调用 pthread_join
    • 调度策略:设置线程的调度策略(如 SCHED_FIFO、SCHED_RR 或 SCHED_OTHER)。
    • 调度优先级:设置线程的调度优先级。
    • 继承性:指定新线程是否继承创建它的线程的信号掩码

pthread_attr_init

  • 线程属性初始化
接口代码
#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);
参数解释
  • attr:

    • 类型:pthread_attr_t *

    • 描述:这是一个指向 pthread_attr_t 类型的指针,表示要初始化的线程属性对象。

返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码。常见的错误码包括:
    • EINVALattr 指向无效的地址。
    • ENOMEM:系统资源不足,无法初始化线程属性对象。

pthread_attr_setdetachstate

  • 线程属性修改函数
接口代码
#include <pthread.h>

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数解释
  • attr:
    • 堆栈大小:设置线程的堆栈大小。
    • 堆栈地址:指定线程堆栈的起始地址。
    • 分离状态:指定线程是否是分离的(detached)。分离的线程在终止时会自动释放资源,不需要调用 pthread_join
    • 调度策略:设置线程的调度策略(如 SCHED_FIFO、SCHED_RR 或 SCHED_OTHER)。
    • 调度优先级:设置线程的调度优先级。
    • 继承性:指定新线程是否继承创建它的线程的信号掩码。
  • detachstate:这是要设置的分离状态。可以取以下两个值之一:
    • PTHREAD_CREATE_JOINABLE:线程在终止时保持可连接状态。这意味着其他线程可以通过调用 pthread_join 来等待该线程结束,并获取其退出状态。
    • PTHREAD_CREATE_DETACHED:线程在终止时会自动释放所有资源,并且不能被其他线程通过 pthread_join 等待。这种状态下,线程一旦终止,其资源会被立即回收。
返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码。常见的错误码包括:
    • EINVALattr 指向无效的地址,或者 detachstate 的值不是 PTHREAD_CREATE_JOINABLEPTHREAD_CREATE_DETACHED
pthread_attr_destroy
  • 线程属性销毁
接口代码
#include <pthread.h>

int pthread_attr_destroy(pthread_attr_t *attr);
参数解释
  • attr: 定义线程属性对象
返回值
  • 成功:返回 0。
  • 失败:返回一个非零错误码。

练习–线程属性设置

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h> // for sleep

// 线程启动函数
void *thread_function(void *arg)
{
    int thread_id = *(int *)arg;

    // 打印线程 ID
    printf("线程 ID: %d, 正在运行\n", thread_id);

    // 模拟一些工作
    sleep(2);

    return NULL;
}

int main()
{
    pthread_t thread;
    int id = 1;
    pthread_attr_t attr; // 定义线程属性对象

    // 初始化线程属性对象
    if (pthread_attr_init(&attr) == -1)
    {
        perror("初始化失败");
        exit(1);
    }

    // 设置线程为分离状态
    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
    {
        perror("线程分离设置失败");
        exit(1);
    }

    // 创建线程
    if (pthread_create(&thread, &attr, thread_function, &id) == -1)
    {
        perror("线程创建失败");
        exit(1);
    }

    // 主线程继续执行
    printf("主线程 ID: %lu\n", pthread_self());

    // 由于线程是分离状态,所以不需要调用 pthread_join

    // 销毁线程属性对象
    if (pthread_attr_destroy(&attr) == -1)
    {
        perror("线程对象销毁失败");
        exit(1);
    }

    // 主线程可以继续执行其他任务
    sleep(3); // 等待一段时间,确保子线程完成

    exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值