Linux线程

    xiaoxiao2023-10-21  26

    线程

    线程: 也称轻量级进程,是程序执行流的最小单元。而多线程就是指,在一个进程中有多个执行流,在同时执行。进程是承担调度的基本单位,一个进程可拥有多个线程,它的执行力度比进程更加细致,线程资源共享。

    注意: 在Linux中并不存在真正的线程,Linux测线程是使用进程模拟的。我们在Linux系统中,线程的创建实在内核外进行的,有POSIX提供的线程库实现。因此链接这些线程函数库时要使用编译器命令的“-lphtread”选项。

    线程的本质: 在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone(),该系统copy了一个和原进程完全一样的进程,并在这个进程中执行线程函数,不过这个copy过程和fork不一样,copy后的进程和原来的进程共享了所有的变量,运行环境。所以线程是共享全局变量和环境的。

    线程和进程的区别: 进程是资源竞争的基本单位,线程是程序执行的最小单位,是承担调度的基本单位。操作系统中每一个执行的进程,都有它自己的地址空间,而同一进程中可以有多个线程,也就是多个执行流在同时执行。这里的同时,如果是单核处理器,则并不是真正意义上的同时,由于处理器运行速度很快,给每个执行流分配了时间片,在单核处理器微观上还是顺序执行,而在多核处理器中,就是真正意义上的并行。由于同一进程的多个线程共享同一个地址空间,因此线程之间有互相共享资源。但是也拥有自己独立的部分:线程ID,独立的上下文数据,栈,errno,信号屏蔽字,调度优先级。

    线程之间共享的资源主要有: 1.地址空间 2.数据段和代码段 3.全局变量 4.文件描述符表 5.信号处理方式(忽略或者有自定义动作) 6.用户ID和组ID 7.当前工作目录

    线程独享资源主要有: 1.线程ID 2.上下文,包括各种寄存器的值,程序计数器和栈指针 3.栈空间 4.errno变量 5.信号屏蔽字 6.调度优先级

    线程与进程的区别归纳如一下几点: 1.地址空间:进程间相互独立,每个进程都有自己独立的地址空间,同一进程的各线程间共享地址空间。某个进程内的线程在其他进程内不可见。 2.通信关系:进程间通信有管道,消息队列,共享内存,信号量。线程间通信可以直接读写全局变量来进行通信。不管是进程还是线程,通信时可能出现数据不一致的情况,需要用同步互斥机制来保证数据的一直性。 3.切换和调度:由于进程间独占数据段代码等信息,所以切换进程的时候,需要把进程间独占的资源切换出去,把需要执行的进程资源换进来,而线程的进程的子集,共享大部分资源,切换时只需要保存上下文相关信息就好,所以线程切换的开销比进程切换的开销小。 4.线程是处理器调度的基本单位,但进程不是 5.二者均可并发执行

    线程的状态 就绪:线程具备运行的所有条件,逻辑上已可以运行,在等待处理机 阻塞:指线程在等待某一时间的发生,如I/O操作 运行:占有处理机正在运行

    线程的优点: 1.创建进程消耗的资源更少 2.线程切换的开销更少 3.充分利用多处理器的可并行数量 4.线程占用的资源比进程少 5.在等待慢速I/O操作结束的同时,程序可以执行其他的就算任务 6.计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现 7.I/O密集型应用,为了能提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

    线程的缺点 1.如果计算密集型线程比可用处理器多,有可能增加了额外的同步和调度开销,而可用的资源不变 2.健壮性降低:编写多线程程序需要考虑的更加全面,有可能共享了不该共享的变量,造成不良影响 3.缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些系统函数对整个进程造成影响 4.编写程序难度较高

    主要的函数列表

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

    参数: thread返回线程ID; attr设置线程属性,默认为NULL; start_routine一个函数地址,线程启动后执行的函数; arg传给执行的函数的参数 返回值:成功返回0,失败返回错误码。

    #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<pthread.h> void *func(void *arg) { int n = *(int*)(arg); while (1) { printf("child thread n = %d\n", n); sleep(1); } } int main() { int n = 5; pthread_t tid; int ret; ret = pthread_create(&tid, NULL, func, (void*)&n); if (ret != 0) { fprintf(stderr, "pthread_create:%s\n", strerror(ret)); exit(-1); } while (1) { printf("main thread\n"); sleep(1); } }

    进程ID和线程ID 在Linux中,目前的线程是靠POSIX线程库和进程实现的,在这种实现下,线程又被称为轻量级进程,每一个用户态的线程,在内核中都有一个对应的调度实体,也有自己的task_struct结构体。   在没有线程之前,一个进程对应内核里的一个task_struct,对应一个进程ID;但是在引入线程之后,一个进程下有n个用户态线程,每个线程作为独立的调度实体有自己的进程描述符,这样,进程和内核中的进程描述符变成了1:n关系,但是POSIX标准要求所有的线程调用getpid时返回相同的进程ID。此时,就有了线程组的概念。 struct task_struct { … pid_t pid; pid_t tgid; … struct task_struct *group_leader; … struct list_head thread_group; … }; 多线程的进程,又被称为线程组,线程组内的每一个线程在内核中存在一个进程描述符与之对应。其实在进程描述符中pid,描述的线程ID,其中的tgid对应的才是用户层的进程ID。

    最新回复(0)