1. 什么是线程

  线程是进程执行内部的一个执行分支,在一个进程内部运行的多种执行流;内部本质上是多个线程在同一个地址空间运行;第一个pcb称之为主线程;有多个线程就有多个执行流;一个进程至少有一个线程

2. 图解线程

  1. PCB2所代表的进程通过vfork创建一个子进程,子进程再vfork一个新的子进程,以此类推产生两个新的子进程;

  2. 此时PCB1、PCB2、PCB3都指向同一块虚拟地址空间,通过操作把PCB1所指向的虚拟空间的资源(主要是数据和代码),分成3部分分别给PCB1、PCB2、PCB3

  3. 这些进程就被称为线程,这些线程之间满足互不干扰的条件,且这些线程共用同一虚拟空间,并且共用部分资源,在访问这些公共资源时,这些线程可以互相访问到对方的资源

3. linux下的线程

  linux下并没有真正意义上的线程存在,linux中使用进程来模拟实现线程,父进程创建子进程,子进程执行父进程的一部分代码,并且与父进程共享同一个地址空间。这些一个一个被创建出来的子进程可看到为线程,这种线程也称之为轻量级进程

注:轻量级进程(LWP)是一种实现多任务的方法。与普通进程相比,LWP与其他进程共享所有(或大部分)它的逻辑地址空间和系统资源;linux下,CPU看到的所有进程都可以看作为轻量级进程

4. 线程的资源(私有和共享)

共享 私有
数据段,代码段 每个线程都有自己的栈结构
文件描述符表 上下文,(包含各种计数器的值、程序计数器和栈指针)
每种信号的处理方式 线程id
当前工作的目录 errno变量(当线程异常退出时的错误退出码)
用户id和线程组id 信号屏蔽字
  调度优先级

5. 线程的优缺点

优点

  1. 线程占用的资源比进程少,只虚复制PCB即可
  2. 创建时代价较小
  3. 线程间的切换(调度)需要操作系统做的工作少很多
  4. 线程间共享数据更容易
  5. 在等待慢速 I/O操作结束的同时,程序可执行其他的计算任务。
  6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
  7. I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

注:关于I/O密集型和计算密集型可参考这篇文章:CPU-bound(计算密集型) 和I/O bound(I/O密集型

缺点

  1. 性能损失(一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享一个处理器。如果计算密集型线程的数量比可用的处理器多,那么就有可能造成较大的性能损失,这里的性能损失指的是操作系统增加了额外的同步和调度开销,而可用的资源不变);

  2. 健壮性降低(由于线程是共享同一块虚拟地址空间的,在运行期间,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,也就是说线程之间是缺乏保护的);

  3. 缺乏访问控制(当在线程中调用某些函数(OS函数,处理signal函数,调用kill/exit函数),可能会导致线程退出,从而使所有的线程都异常退出);

  4. 编程难度提高(线程共用同一块虚拟地址空间,势必在处理多线程时会有访问同一个资源等问题,此时就涉及到共享资源的处理)


6. 线程控制

6.0 写在前面

  在操作系统内部,它不管什么进程线程的,它只以PCB为准,只有在用户态里才有线程的概念。一般实现线程会用到一个POSIX线程库,在这里可以通过调用POSIX库里的函数来实现有关线程的各种操作。不过内核中也有一种内核级线程。

  两个基本类型:

  1. 用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。如POSIX线程库。

  2. 系统级线程(核心级线程):由操作系统内核进行管理。操作系统内核给应用程序提供相应的系统调用和应用程序接口API,以使用户程序可以创建、执行、撤消线程。

POSIX线程库

  由系统库支持。线程的创建和撤销以及线程状态的变化都由库函数控制并在目态(user态)完成,与线程相关的控制结构TCB保存在目态并由系统维护。由于线程对操作不可见(操作系统可见的必然保存在kernel态由系统维护),系统调度仍以进程为单位(同一进程内线程相互竞争),核心栈的个数与进程个数相对性。

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的

  • 要使用这些库函数,就要引入头文件

  • gcc在链接这些线程函数库时要使用编译器命令的“-lpthread”选项(pthread是共享库文件)

6.1 创建线程

注:创建出新线程后,新线程去执行函数,主线程继续往下运行,谁先谁后不一定,同理fork父子进程

#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);//参数:
//thread:线程id,将线程id存入,线程标识符,线程栈的起始地址,输出型参数
//attr:线程属性,NULL,8种选项
//函数指针:新线程执行该函数指针指向的代码,线程回调函数
//arg:传递给函数指针指向的函数的参数,线程回调函数的参数//返回值:成功返回0,失败返回错误码,如:
//EAGAIN   描述: 超出了系统限制,如创建的线程太多。
//EINVAL   描述: tattr 的值无效。

例:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>void* thread_func(void* arg)
{(void)arg;while(1){printf("I am new thread\n");sleep(1);}
}int main()
{pthread_t tid;if ( pthread_create(&tid,NULL,thread_func,NULL) == -1){perror("pthread_create");exit(1);}while(1){printf("I am  main thread\n");sleep(1);}return 0;
}

结果:

6.2 线程ID

  线程是进程的一个执行分支,并且线程在内核中的存在状态是轻量级进程,因此线程ID和进程ID存在一定的关系,可查看:linux下的线程ID和进程ID

6.3 线程的查看以及多线程的调试

  线程是进程的执行分支,多个线程共享同一块虚拟地址空间,在这其中,多个线程共享数据段、 代码段等等,但还是存在自己私有的一些结构,对于这些结构,我们可以通过不同的方法可以进行查看。可参考:线程的查看以及多线程的调试

6.4 等待线程——pthread_join


  1. 功能:以阻塞的方式回收新线程,可以得到线程的退出码,并回收其资源
  2. 如果不使用pthread_join回收线程,有可能造成和僵尸进程一样的问题,造成内存泄漏;
    #include <pthread.h>
    int pthread_join(pthread_t thread, //要等待的线程ID
    void **retval);//用于接收线程退出的退出码

等待线程的目的:

  1. 保证线程的退出顺序:保证一个线程退出并且回收资源后允许下一个进程退出
  2. 回收线程退出时的资源情况:保证当前线程退出后,创建的新线程不会复用刚才退出线程的地址空间
  3. 获得新线程退出时的结果是否正确的退出返回值,这个有点类似回收僵尸进程的wait,保证不会发生内存泄露等问题

6.5 终止线程

 方式1:在一个线程中return

  如果线程通过return返回,那么retval所指向的单元里存放的是tread函数的返回值

例:main函数创建一个新线程,新线程执行完自己的函数,使用return退出,那么返回值就是退出码

方式2:exit

  如果线程是自己调用exit终止的,那么就是直接退出,并且exit表示进程的退出

exit和return的区别:

  1. return是函数的退出,exit是进程的退出

  2. return执行结束后会调用exit或和exit类似的函数,return会释放局部变量并且弹出栈桢,回到上一个函数继续执行

方式3: 使用pthread_exit()

  线程自己调用函数终止,pthread_ jion()函数里的retval(退出码)就是pthread_exit的参数

#include <pthread.h>
void pthread_exit(void *retval);

线程的退出函数,retval是退出码,该函数只是当前线程退出,不影响其他线程

方式4:

  如果线程是通过pthread_ cancel被别的线程异常终止,则retval(退出码)就是PTHREAD_ CANCELED

#include <pthread.h>int pthread_cancel(pthread_t thread);//线程ID
//可以终止自己的线程也可以终止别人的线程
//成功返回0,失败返回-1
//终止自己算是成功退出,发回0,终止别的进程,则那个进程算是失败退出,返回-1;
//不是立马执行,会延迟一会,等到cancel的时间点
//系统调用的都是cancel点

例1:使用return退出


#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>void* thread_func(void* arg)
{(void)arg;int count = 0;while(1){++count;if(count > 10)return NULL;//使用return方式退出printf("I am new thread:%d,count: %d\n",pthread_self(),count);sleep(1);}
}int main()
{pthread_t tid;if ( pthread_create(&tid,NULL,thread_func,NULL) == -1){perror("pthread_create");exit(1);}while(1){printf("I am  main thread:%d\n",pthread_self());sleep(1);}return 0;
}

结果:

例2:exit退出


#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>void* thread_func(void* arg)
{(void)arg;int count = 0;while(1){++count;if(count > 10)exit(1);//使用return方式退出printf("I am new thread:%d,count: %d\n",pthread_self(),count);sleep(1);}
}int main()
{pthread_t tid;if ( pthread_create(&tid,NULL,thread_func,NULL) == -1){perror("pthread_create");exit(1);}while(1){printf("I am  main thread:%d\n",pthread_self());sleep(1);}return 0;
}

结果:

例3:pthread_exit退出

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>void* thread_func(void* arg)
{(void)arg;int count = 0;while(1){++count;if(count > 10)pthread_exit(NULL);printf("I am new thread:%d,count: %d\n",pthread_self(),count);sleep(1);}
}int main()
{pthread_t tid;if ( pthread_create(&tid,NULL,thread_func,NULL) == -1){perror("pthread_create");exit(1);}while(1){printf("I am  main thread:%d\n",pthread_self());sleep(1);}return 0;
}

结果:

例4:pthread_cancel

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>void* thread_func(void* arg)
{(void)arg;while(1){printf("I am new thread:%d\n",pthread_self());sleep(1);}
}int main()
{pthread_t tid;if ( pthread_create(&tid,NULL,thread_func,NULL) == -1){perror("pthread_create");exit(1);}int count = 0;void* ret;while(1){++count;if(count > 5){pthread_cancel(tid);pthread_join(tid,&ret);}if(ret == PTHREAD_CANCELED)printf("thread is return,thread id: %d, return code:PTHREAD_CANCELED\n",tid);elseprintf("thread is return,thread id: %d, return code:NULL\n",tid);printf("I am main thread: %d,count: %d\n",pthread_self(),count);sleep(1);}return 0;
}

结果:


6.6 线程分离

   6.6.1 线程的两种状态——可结合、可分离
  
  线程分为两种状态:可结合态和分离态;默认情况下,线程被创建成可结合的。

1. 可结合态:

  1. 这种状态下的线程是能够被其他进程回收其资源或杀死的,这句话我的理解是:与其说它能够被其他进程回收或杀死,不如说它需要被其他进程回收或杀死;当它在被其他线程回收之前,它的存储器资源(如栈)是不会释放的;
  2. 这跟子进程很相似,如果不用父进程wait回收的话,就会变成僵尸进程同理,如果一个可结合态线程不用pthread_join回收,则会变成类似僵尸进程

2. 分离态

  1. 这种状态下的线程是不能够被其他线程回收或杀死的;它的存储资源在它终止时由系统自动释放
  2. 为了避免存储器泄漏,每个可结合线程需要显示的调用pthread_join回收;>要么就将其变成分离态的线程

  6.6.2 线程分离函数—–pthread_detach

#include <pthread.h>
int pthread_detach(pthread_t thread);
//将pthread_thread对应的线程设为分离态的线程

pthread_detach的两种用法:

  1. 新线程中写:pthread_detach(pthread_self());
  2. 主线程中写:pthread_detach(thread);

注:多个线程,是在同一个进程中的,它们都共享着同一块虚拟空间,第一种方法是将自己从这些线程中分离出来;
第二种方法是将指定的线程从这些线程中分离出去;简单来说就是,一个是自己把自己弄出去,一个是让别人把自己弄出去

linux c 线程的创建、线程等待、线程终止、线程分离相关推荐

  1. java 父线程_Java父线程(或是主线程)等待所有子线程退出的实例

    导读热词 实例如下: static void testLock1(){ final AtomicInteger waitCount = new AtomicInteger(30000); final ...

  2. linux下多线程的创建与等待详解 【转载】

    linux下多线程的创建与等待详解 http://blog.chinaunix.net/uid-23842323-id-2656572.html 所有线程都有一个线程号,也就是Thread ID.其类 ...

  3. 线程的控制(创建、等待、终止)、分离线程

    一.线程控制 1.线程:线程是资源调度的基本单位,线程是进程内部的一个执行流,在进程的地址空间内运行.在Linux 下没有真正意义上的线程,线程是用进程模拟的,又被称为轻量级进程. 2.由于同⼀一进程 ...

  4. 线程池创建与注入,以及线程池参数失效

    一.MyThreadPool public class MyThreadPool {/*** 自定义线程名称,方便的出错的时候溯源*/private static ThreadFactory name ...

  5. 线程的创建开销大吗?线程创建开销包括哪些?线程池

    1-1. 关于时间,创建线程使用是直接向系统申请资源的,这里调用系统函数进行分配资源的话耗时不好说. 关于资源,Java线程的线程栈所占用的内存是在Java堆外的,所以是不受java程序控制的,只受系 ...

  6. pyqt stop停止线程_面试官:如何终止线程?有几种方式?

    在 Java 中有以下 3 种方法可以终止正在运行的线程: 使用退出标志,使线程正常退出,也就是当 run() 方法完成后线程终止: 使用 stop() 方法强行终止线程,但是不推荐使用这个方法,因为 ...

  7. linux创建进程读共享写复制,Linux下进程的创建、执行和终止

    1)进程的创建和执行 许多操作系统提供的都是产生进程的机制,也就是说,首先在新的地址空间里创建进程.读入可执行文件,后再开始执行.Linux中进程的创建很特别,它把上述步骤分解到两个单独的函数中去执行 ...

  8. Linux中进程的创建、进程的终止、进程的等待、进程的程序替换

    进程的创建 在进程的创建中,我们一个非常重要的函数 fork()函数,fork()函数会创建一个新的进程,为原有进程的子进程,原有就为父进程. 我们来看一下fork()函数的原型. #include ...

  9. Linux 线程的创建与同步

    Linux 线程的创建与同步 1.线程的定义 2.线程的创建和使用 3.理解线程的并发运行 3.线程同步 3.线程的实现 1.线程的定义 线程:进程内部的一条执行路径.是资源调度和执行的基本单位. 进 ...

  10. 如何高效放鸽子——线程的创建_莫韵乐的Linux王国

    如何高效放鸽子--线程的创建.等待和销毁 前文再续,书接上一回 今天我们将会采访一位放鸽子高手,探讨如何高效地放放鸽子 记者:您好,真是百闻不如一见,没想到传说中的鸽王相貌平平却有如此大的能耐 鸽王: ...

最新文章

  1. R语言可视化绘制基本图形
  2. 企业网络高级技术第二章STP实验
  3. SAP Hybris Commerce Cloud Accelerator Storefront 在 Eclipse 中的调试
  4. Redis工作笔记-Redis安装及基本配置
  5. C++ 临时变量的常量性
  6. ros简版Action通讯SimpleAction
  7. .Net_asp.net页面的生命周期
  8. java rpg对战_java实现模拟RPG格斗
  9. Module not found: Error: Can‘t resolve ‘vue-router‘ in
  10. QtreeView 树形结构
  11. QT界面窗口的显示和隐藏,关闭
  12. VUM升级esxi主机6.0至6.7U3失败-无法再主机上执行升级脚本
  13. python 伯努利分布
  14. 略谈“10步天才(10 step)思维模型”
  15. html5视频在线剪辑,五种剪辑方法让视频更精彩
  16. 最新版表情包小程序源码无需服务器
  17. 洛谷P1424 小鱼的航程(改进版)-c++题解
  18. 【vs quick】winmm.lib 解决 timeBeginPeriod 找不到
  19. 2022年全球及中国疏水阀行业头部企业市场占有率及排名调研报告
  20. 《ZigBee开发笔记》第五部分 外设篇 - 基础实验 第5章 CC2530继电器模块

热门文章

  1. C 编程异常 — double free or corruption (fasttop)
  2. 微服务测试之性能测试
  3. Android NDK JNI WARNING: illegal start byte 0x
  4. 关于mysql的调优
  5. SpringMVC、MyBatis声明式事务管理
  6. Linux awk内部变量
  7. UINavigationController 返回到各级目录
  8. 细节之处方显linux真功夫
  9. 让你的PHP更安全之PHP.ini
  10. IPsec NAT穿越