【Linux系统编程】多线程与自身相关api


Linux中的线程(Threads)是轻量级进程(Lightweight Processes,LWP),它们共享相同的地址空间和大部分资源,但每个线程都有自己独立的栈、寄存器和程序计数器。线程允许程序在单个进程内并发执行多个任务,提高程序的执行效率和响应速度。下面是对Linux线程的一些基本介绍

线程与进程

线程是应用程序并发执行多种任务的一种机制。在一个进程中可以创建多个线程执行多个任务。每个进程都可以创建多个线程,创建的多个线程共享进程的地址空间,即线程被创建在进程所使用的地址空间上。创建子进程是通过复制父进程的地址空间得来的,父子进程只关注自己的地址空间(映射不同的物理地址空间)。因此进程与进程之间是独立的,每个进程都只需要操作属于自己的地址空间即可。而线程则不一样,创建线程无须对地址空间进行复制,同一个进程创建的线程共享进程的地址空间,因此创建线程比创建子进程要快很多。
如图所示,一个进程可以包含多个线程。因此,同一个程序中的所有线程均会执行相同的程序,且共享进程的内存段,包括数据段、堆区。值得注意的是,进程的栈区对线程是不共享的,每个线程都拥有属于自己的栈区,用于存放函数的参数值、局部变量的值、返回地址等。
在这里插入图片描述

使用多线程的理由

使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。

使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:

  • 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time
    consuming)置于一个新的线程,可以避免这种尴尬的情况。
  • 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
  • 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改

多线程开发API概要

多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。详细请见下表:
在这里插入图片描述

pthread_detach分离线程函数

在Linux多线程编程中,pthread_detach是一个函数,用于将一个线程设置为分离状态(detached state)。这意味着该线程在结束时会自动释放其资源,而不需要其他线程显式地清理这些资源。这使得分离的线程非常适合那些不需要等待其完成的任务。

函数原型

#include <pthread.h>

int pthread_detach(pthread_t thread);

thread: 是要分离的线程的标识符。
返回值
pthread_detach函数返回以下值之一:

  • 0: 成功分离线程。
  • 非零值: 发生错误,并且errno将被设置为相应的错误码。

错误码
如果pthread_detach失败,它可能会设置以下errno值之一:

  • EINVAL: 指定的线程ID无效。
  • ESRCH: 没有找到与给定线程ID匹配的线程。
  • EINVAL: 线程已经分离或已经终止。

使用场景

  • 后台任务:
    当你创建一个线程来执行后台任务,如日志记录或数据同步,这些任务通常不需要等待完成。在这种情况下,你可以分离线程,以便主线程或其他线程可以继续执行而不必等待这些后台任务完成。
  • 一次性任务:
    对于只需要执行一次的任务,分离线程可以简化代码结构,避免需要显式地等待线程完成。

在这里插入图片描述
这样不需要在父线程中join等待它结束了

线程

线程的创建

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

// 线程函数原型:
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);

// 线程函数:线程创建后将运行此函数
void *func1(void *arg)
{
    // 定义一个静态字符指针,指向字符串 "t1 is run out"
    static char *p = "t1 is run out";

    // 打印线程ID(通过 pthread_self 获取)
    printf("t1:%ld thread is create\n", (unsigned long)pthread_self());
    // 打印传入的参数值(通过类型转换和解引用获取)
    printf("t1:param is %d\n", *((int *)arg));

    // 退出线程并返回指针 p
    pthread_exit((void *)p);
}

int main()
{
    int ret; // 存储 pthread_create 的返回值
    int param = 100; // 将作为参数传递给线程函数
    pthread_t t1; // 线程标识符,用于存储创建的线程ID

    char *pret = NULL; // 用于存储线程函数的返回值

    // 创建线程 t1,执行函数 func1,传递参数 &param
    ret = pthread_create(&t1, NULL, func1, (void *)&param);
    if (ret == 0) {
        // 如果线程创建成功,打印成功消息
        printf("main:create t1 success\n");
    }

    // 打印主线程的线程ID
    printf("main:%ld\n", (unsigned long)pthread_self());

    // 等待线程 t1 结束,并获取其返回值
    pthread_join(t1, (void **)&pret);

    // 打印线程 t1 的返回值
    printf("main: t1 quit: %s\n", pret);

    return 0; // 返回0表示程序正常结束
}


互斥锁

#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // 包含sleep函数

// 声明pthread_create函数的原型
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);

int g_data = 0; // 全局变量,用于线程间共享和修改的数据

pthread_mutex_t mutex; // 定义互斥锁,用于保护全局变量g_data的访问

// 线程函数1
void *func1(void *arg)
{
    // 打印线程ID和传入的参数值
    printf("t1:%ld thread is create\n", (unsigned long)pthread_self());
    printf("t1:param is %d\n", *((int *)arg));
    
    pthread_mutex_lock(&mutex); // 加锁,保护g_data的访问
    while(1) {
        printf("t1: %d\n", g_data++); // 打印并增加g_data
        sleep(1); // 休眠1秒

        // 如果g_data等于3,解锁并退出线程
        if (g_data == 3) {
            pthread_mutex_unlock(&mutex); // 解锁
            printf("t1 quit================================\n");
            // pthread_exit(NULL); // 可以使用pthread_exit退出线程
            exit(0); // 退出程序(注意:此行会导致整个进程退出,包括主线程和其他线程)
        }
    }
}

// 线程函数2
void *func2(void *arg)
{
    // 打印线程ID和传入的参数值
    printf("t2:%ld thread is create\n", (unsigned long)pthread_self());
    printf("t2:param is %d\n", *((int *)arg));
    
    while(1) {
        printf("t2: %d\n", g_data); // 打印g_data
        pthread_mutex_lock(&mutex); // 加锁,保护g_data的访问
        g_data++; // 增加g_data
        pthread_mutex_unlock(&mutex); // 解锁
        sleep(1); // 休眠1秒
    }
}

int main()
{
    int ret;
    int param = 100; // 用于传递给线程函数的参数
    pthread_t t1, t2; // 线程标识符

    pthread_mutex_init(&mutex, NULL); // 初始化互斥锁

    // 创建线程t1,执行函数func1,传递参数&param
    ret = pthread_create(&t1, NULL, func1, (void *)&param);
    if (ret == 0) {
        printf("main:create t1 success\n");
    }

    // 创建线程t2,执行函数func2,传递参数&param
    ret = pthread_create(&t2, NULL, func2, (void *)&param);
    if (ret == 0) {
        printf("main:create t2 success\n");
    }

    // 打印主线程的线程ID
    printf("main:%ld\n", (unsigned long)pthread_self());
    while (1) {
        printf("main: %d\n", g_data); // 打印g_data
        sleep(1); // 休眠1秒
    }

    pthread_join(t1, NULL); // 等待线程t1结束
    pthread_join(t2, NULL); // 等待线程t2结束
    pthread_mutex_destroy(&mutex); // 销毁互斥锁

    return 0; // 返回0表示程序正常结束
}

条件

#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // 包含sleep函数

// 声明pthread_create函数的原型
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);

int g_data = 0; // 全局变量,用于线程间共享和修改的数据

// 初始化互斥锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 线程函数1
void *func1(void *arg)
{
    // 打印线程ID和传入的参数值
    printf("t1:%ld thread is create\n", (unsigned long)pthread_self());
    printf("t1:param is %d\n", *((int *)arg));
    static int cnt = 0; // 静态变量,用于记录循环次数

    while(1) {
        // 等待条件变量,等待时释放互斥锁,条件满足后重新加锁
        pthread_cond_wait(&cond, &mutex);
        printf("t1 run================================\n");

        // 打印并重置g_data
        printf("t1: %d\n", g_data);
        g_data = 0;
        sleep(1); // 休眠1秒

        // 循环计数器,达到10次时退出程序
        if (cnt++ == 10) {
            exit(1);
        }
    }
}

// 线程函数2
void *func2(void *arg)
{
    // 打印线程ID和传入的参数值
    printf("t2:%ld thread is create\n", (unsigned long)pthread_self());
    printf("t2:param is %d\n", *((int *)arg));

    while(1) {
        // 打印g_data
        printf("t2: %d\n", g_data);
        pthread_mutex_lock(&mutex); // 加锁,保护g_data的访问
        g_data++; // 增加g_data

        // 如果g_data等于3,发送条件信号
        if (g_data == 3) {
            pthread_cond_signal(&cond);
        }
        pthread_mutex_unlock(&mutex); // 解锁
        sleep(1); // 休眠1秒
    }
}

int main()
{
    int ret;
    int param = 100; // 用于传递给线程函数的参数
    pthread_t t1, t2; // 线程标识符

    // 创建线程t1,执行函数func1,传递参数&param
    ret = pthread_create(&t1, NULL, func1, (void *)&param);
    if (ret == 0) {
        // printf("main:create t1 success\n");
    }

    // 创建线程t2,执行函数func2,传递参数&param
    ret = pthread_create(&t2, NULL, func2, (void *)&param);
    if (ret == 0) {
        // printf("main:create t2 success\n");
    }

    // printf("main:%ld\n", (unsigned long)pthread_self());

    pthread_join(t1, NULL); // 等待线程t1结束
    pthread_join(t2, NULL); // 等待线程t2结束

    pthread_mutex_destroy(&mutex); // 销毁互斥锁
    pthread_cond_destroy(&cond); // 销毁条件变量

    return 0; // 返回0表示程序正常结束
}

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值