Linux多线程

本文详细介绍了Linux下的多线程概念,包括进程与线程的区别、线程的资源共享与独有资源、线程的创建、终止、等待与分离,以及线程安全中的互斥和同步机制。此外,还提供了查看线程的命令,帮助读者更好地理解和掌握Linux多线程编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程与线程的区别

进程是一个运行中的程序,站在操作系统的角度,进程就是一个运行中的长须的描述-PCB(进程控制块)。
线程是轻量级的进程。在传统的操作系统中,pcb就是进程,而tcb是线程控制块,每个线程有他自己的tcb。但是在Linux下,因为线程是通过进程的pcb描述实现的(task_struct结构体),因此Linux下的pcb实际上是一个线程组,并且因为这些线程共用一个虚拟地址空间,因此也把Linux下的线程成为轻量级进程,相较于传统pcb更加的轻量化。
进程是资源分配的基本单位,线程是CPU调度的基本单位。
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要使用进程间通信方式(IPC)进行。

  • 多线程的优点:
    因为共用同一块虚拟地址空间,因此线程间通信更加灵活方便(可以使用全局变量、传参等方式)
    线程间共用进程的大部分资源,因此线程的创建与销毁成本更低。
    也因为线程间共用进程的大部分资源,所以线程的切换调度成本更低。
  • 多线程缺点:
    线程间缺乏访问控制,某些系统调用以及异常是针对整个进程产生效果的。
  • 多进程优点:
    进程间相互独立,因此稳定、健壮性更高,适用于对主程序安全稳定性要求更高的场景,例如shell、服务器。

线程

线程共享的资源

线程共用所属进程的同一地址空间,因此代码段、数据段(虚拟地址空间)都是共享的,定义一个函数,则在各线程中都可以调用,定义一个全局变量,在各线程中都可以访问,除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符表
  • 每种信号的处理方式(SIG_ IGN忽略、SIG_ DFL默认或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id
线程独有的资源

线程共享进程的数据,但也有一部分自己独有的数据:

  • 线程ID,即线程标识符
  • 寄存器(存放上下文数据等)
  • errno
  • 信号屏蔽字(阻塞信号集合)
  • 调度优先级
线程属性pthread_attr_t结构体
typedef struct{
	int detachstate;                //线程的分离状态
	int schedpolicy;                //线程调度策略
	struct sched_param schedparam;  //线程的调度参数
	int inheritsched;               //线程的继承性
	int scope;                      //线程的作用域
	size_t guardsize;               //线程栈末尾的警戒缓冲区大小
	int stackaddr_set;              //线程的栈设置
	void* stackaddr;                //线程栈的位置(最低地址)
	size_t stacksize;               //线程栈的大小
} pthread_attr_t; 

属性值不能直接设置,须使用相关函数进行操作,初始化pthread_attr_init,这个函数必须在pthread_create函数之前调用。
之后须用pthread_attr_destroy函数来释放资源。
线程属性主要包括如下属性:作用域(scope)、栈尺寸(stack size)、栈地址(stack address)、优先级(priority)、分离的状态(detached state)、调度策略和参数(scheduling policy and parameters)。
默认属性为非绑定、非分离、缺省的堆栈、与父进程同样级别的优先级。

线程创建:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

pthread_t:无符号长整型
*thread:线程地址空间,用户态县城描述信息/操作句柄。线程地址空间在进程虚拟地址空间中的首地址。
(pcb中的pid为轻量级进程ID,而tgid为进程ID,即线程组ID,值默认等于主线程的pid)
pthread_attr_t *attr:线程的属性参数,通常置空
void *(*start_routine) (void *), void *arg:线程入口函数,*arg为传递给线程的参数

线程终止:
  1. 线程见自己的入口函数运行完毕return退出,在主函数main函数中退出的是进程。
  2. void pthread_exit(void *retval); 退出调用线程
  3. int pthread_cancel(pthread_t thread); 取消一个指定线程,成功返回0,失败返回错误编号
线程等待:

默认情况下,线程退出后也不会完全释放资源,需要被其他线程等待,线程等待即指等待一个指定的线程退出,retval获取这个退出线程的返回值,并且回收资源。

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
线程分离:

一个线程被创建出来,默认在退出时不会释放所有资源,因为线程的默认情况下的joinable属性。处于joinable状态的线程,在退出时不会自动释放资源,需要被等待。
线程分离即将一个线程的属性从joinable设置为detach,表示分离一个线程,被分离处于detach的线程在退出后,会自动释放所有资源,故它不需要被等待。

#include <pthread.h>
int pthread_detach(pthread_t thread);
pthread_t pthread_self(void);  // 获取调动线程的线程ID

线程安全

多个执行流(线程间)对同一个临界资源进行争抢访问,但是不会造成数据二义或逻辑混乱,这样才是安全的线程。
线程安全主要靠同步与互斥实现。
同步是通过条件判断实现对临界资源访问的时序和理性,不能访问则等待,能够访问则唤醒。
互斥是同一时间只能有一个一个执行流能够访问临界资源,实现数据的操作安全。

互斥

互斥的实现:互斥锁、信号量

互斥锁的互斥实现原理:互斥锁相当于一个只有0/1的计数器用于标记当前临界资源的访问状态,对于临界资源的访问之前都需要先加锁访问这个状态计数,如果可以进行访问则修改这个计数(计数操作是一个原子操作,通过使用一个交换指令完成寄存器与内存之间的数据交换)。

// 互斥锁的使用
// 黄牛抢票
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#define MAX_THR 4
int tickets = 200;
pthread_mutex_t mutex;

void *fun(void *arg)
{
        while(1)
        {
                // pthread_mutex_lock 阻塞加锁
                // pthread_mutex_trylock 非阻塞加锁
                // pthread_mutex_timedlock 限制阻塞时长的阻塞加锁
                pthread_mutex_lock(&mutex);
                if (tickets > 0)
                {
                        printf("%p get a ticket: %d\n", pthread_self(), tickets);
                        usleep(1000);
                        tickets--;
                        pthread_mutex_unlock(&mutex);
                }
                else
                {
                        printf("there are no tickets\n");
                        // 加锁之后要在所有有可能退出线程的地方解锁
                        pthread_mutex_unlock(&mutex);
                        pthread_exit(NULL);
                }
        }
        return NULL;
}
int main()
{
        pthread_t tid[MAX_THR];
        // pthread_mutex_init() 函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如>果参数attr为空(NULL),则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指
定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现>不同。
        pthread_mutex_init(&mutex, NULL);
        for (int i = 0; i < MAX_THR; i++)
        {
                // 第一个参数为指向线程标识符的指针
                // 第二个参数用来设置线程属性
                // 第三个参数是线程运行函数的起始地址
                // 最后一个参数是运行函数的参数
                int ret = pthread_create(&tid[i], NULL, fun, NULL);
                if (ret != 0)
                {
                        perror("pthread create error");
                        return -1;
                }
        }
        for (int i = 0; i < MAX_THR; i++)
                pthread_join(tid[i], NULL);
        pthread_mutex_destroy(&mutex);
        return 0;
}

同步

同步的实现:条件变量、信号量

条件变量提供了一个让线程等待与唤醒的功能,向外提供一个等待队列,以及等待与唤醒的功能接口。
条件变量并没有条件判断的功能,不具备判断什么时间线程该等待或唤醒,因此条件判断需要用户自身完成。
而条件变量通常需要与互斥锁搭配使用,因为用于判断是否等待的条件是一个临界资源。
pthread_cond_wait操作集合了解锁、休眠、加锁三个操作。
用户的条件判断需要使用while循环判断,因为被唤醒的多个进程可能都等待在锁上,解锁后有可能线程会在不具备访问条件的情况下加锁进行访问。
不同的角色要使用多个条件变量等待在不同的队列中。

// 条件变量的使用
// 生产者a 消费者b
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

int cond = 0;
pthread_cond_t cond_a;
pthread_cond_t cond_b;
pthread_mutex_t mutex;

void *funb(void *arg)
{
        while (1)
        {
                pthread_mutex_lock(&mutex);
                while (cond == 0)
                        pthread_cond_wait(&cond_b, &mutex);
                printf("b -> cond-1\n");
                cond--;
                pthread_mutex_unlock(&mutex);
                pthread_cond_signal(&cond_a);  // 至少唤醒一个
                // pthread_cond_broadcast 广播唤醒所有
        }
        return NULL;
}

void *funa(void *arg)
{
        while (1)
        {
                pthread_mutex_lock(&mutex);
                while (cond == 1)
                        pthread_cond_wait(&cond_a, &mutex);
                printf("a -> cond+1\n");
                cond++;
                pthread_mutex_unlock(&mutex);
                pthread_cond_signal(&cond_b);
        }
        return NULL;
}

#define MAX 5
int main()
{
        pthread_t aTid, bTid;
        int ret = 0;

        pthread_cond_init(&cond_a, NULL);
        pthread_cond_init(&cond_b, NULL);
        pthread_mutex_init(&mutex, NULL);
        for (int i = 0; i < MAX; i++)
        {
                ret = pthread_create(&bTid, NULL, funb, NULL);
                if (ret != 0)
                {
                        perror("thread create b error");
                        return -1;
                }
        }
        ret = pthread_create(&aTid, NULL, funa, NULL);
        if (ret != 0)
        {
                perror("thread create a error");
                return -1;
        }

        pthread_join(aTid, NULL);
        pthread_join(bTid, NULL);
        pthread_cond_destroy(&cond_a);
        pthread_cond_destroy(&cond_b);
        pthread_mutex_destroy(&mutex);
        return 0;
}

查看线程命令

查看当前运行的进程:ps aux
查看当前运行的轻量级进程:ps -aL
查看主线程和新线程的关系:pstree -p 主线程id
线程的查看以及利用gdb调试多线程

内容概要:该论文深入研究了液压挖掘机动臂下降势能回收技术,旨在解决传统液压挖掘机能耗高的问题。提出了一种新型闭式回路势能回收系统,利用模糊PI自整定控制算法控制永磁无刷直流电动机,实现了变转速容积调速控制,消除了节流和溢流损失。通过建立数学模型和仿真模型,分析了不同负载下的系统性能,并开发了试验平台验证系统的高效性和节能效果。研究还涵盖了执行机构能量分布分析、系统元件参数匹配及电机控制性能优化,为液压挖掘机节能技术提供了理论和实践依据。此外,通过实验验证,该系统相比传统方案可降低28%的能耗,控制系统响应时间缩短40%,为工程机械的绿色化、智能化发展提供了关键技术支撑。 适合人群:从事工程机械设计、制造及维护的工程师和技术人员,以及对液压系统节能技术感兴趣的科研人员。 使用场景及目标:①理解液压挖掘机闭式回路动臂势能回收系统的原理和优势;②掌握模糊PI自整定控制算法的具体实现;③学习如何通过理论建模、仿真和实验验证来评估和优化液压系统的性能。 其他说明:此研究不仅提供了详细的理论分析和数学建模,还给出了具体的仿真代码和实验数据,便于读者在实际工作中进行参考和应用。研究结果表明,该系统不仅能显著提高能源利用效率,还能延长设备使用寿命,降低维护成本,具有重要的工程应用价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值