7.9-uc-第九课:线程管理

xuxaut-558 xuxaut-558     2023-01-27     304

关键词:

================
第九课  线程管理
================

一、基本概念
------------

1. 线程就是程序的执行路线,即进程内部的控制序列,
   或者说是进程的子任务。

2. 线程,轻量级,不拥有自己独立的内存资源,
   共享进程的代码区、数据区、堆区(注意没有栈区)、
   环境变量和命令行参数、文件描述符、信号处理函数、
   当前目录、用户ID和组ID等资源。

3. 线程拥有自己独立的栈,因此也有自己独立的局部变量。

4. 一个进程可以同时拥有多个线程,
   即同时被系统调度的多条执行路线,
   但至少要有一个主线程。

二、基本特点
------------

1. 线程是进程的一个实体,
   可作为系统独立调度和分派的基本单位。

2. 线程有不同的状态,系统提供了多种线程控制原语,
   如创建线程、销毁线程等等。

3. 线程不拥有自己的资源,只拥有从属于进程的全部资源,
   所有的资源分配都是面向进程的。

4. 一个进程中可以有多个线程并发地运行。
   它们可以执行相同的代码,也可以执行不同的代码。

5. 同一个进程的多个线程都在同一个地址空间内活动,
   因此相对于进程,线程的系统开销小,任务切换快。

6. 线程间的数据交换不需要依赖于类似IPC的特殊通信机制,
   简单而高效。

7. 每个线程拥有自己独立的线程ID、寄存器信息、函数栈、
   错误码和信号掩码。

8. 线程之间存在优先级的差异。

三、POSIX线程(pthread)
----------------------

1. 早期厂商各自提供私有的线程库版本,
   接口和实现的差异非常大,不易于移植。

2. IEEE POSIX 1003.1c (1995)标准,
   定义了统一的线程编程接口,
   遵循该标准的线程实现被统称为POSIX线程,即pthread。

3. pthread包含一个头文件pthread.h,
   和一个接口库libpthread.so。

#include <pthread.h>

...

gcc ... -lpthread

4. 功能

1) 线程管理:创建/销毁线程、分离/联合线程、
   设置/查询线程属性。

2) 线程同步

A. 互斥量:创建/销毁互斥量、加锁/解锁互斥量、
   设置/查询互斥量属性。

B. 条件变量:创建/销毁条件变量、等待/触发条件变量、
   设置/查询条件变量属性。

四、线程函数
------------

1. 创建线程
~~~~~~~~~~~

int pthread_create (pthread_t* restrict thread,
    const pthread_attr_t* restrict attr,
    void* (*start_routine) (void*),
    void* restrict arg);

thread        - 线程ID,输出参数。
                pthread_t即unsigned long int。

attr          - 线程属性,NULL表示缺省属性。
                pthread_attr_t可能是整型也可能是结构,
                因实现而异。

start_routine - 线程过程函数指针,
                参数和返回值的类型都是void*。
                启动线程本质上就是调用一个函数,
                只不过是在一个独立的线程中调用的,
                函数返回即线程结束。

arg           - 传递给线程过程函数的参数。
                线程过程函数的调用者是系统内核,
                而非用户代码,
                因此需要在创建线程时指定参数。

成功返回0,失败返回错误码。

注意:

1) restrict: C99引入的编译优化指示符,
   提高重复解引用同一个指针的效率。

2) 在pthread.h头文件中声明的函数,
   通常以直接返回错误码的方式表示失败,
   而非以错误码设置errno并返回-1。

3) main函数即主线程,main函数返回即主线程结束,
   主线程结束即进程结束,
   进程一但结束其所有的线程即结束。

4) 应设法保证在线程过程函数执行期间,
   其参数所指向的目标持久有效。

创建线程。范例:create.c

线程并发。范例:concur.c

线程参数。范例:arg.c

2. 等待线程
~~~~~~~~~~~

int pthread_join (pthread_t thread, void** retval);

等待thread参数所标识的线程结束,
成功返回0,失败返回错误码。

范例:ret.c

注意从线程过程函数中返回值的方法:

1) 线程过程函数将所需返回的内容放在一块内存中,
   返回该内存的地址,保证这块内存在函数返回,
   即线程结束,以后依然有效;

2) 若retval参数非NULL,
   则pthread_join函数将线程过程函数所返回的指针,
   拷贝到该参数所指向的内存中;

3) 若线程过程函数所返回的指针指向动态分配的内存,
   则还需保证在用过该内存之后释放之。

3. 获取线程自身的ID
~~~~~~~~~~~~~~~~~~~

pthread_t pthread_self (void);

成功返回调用线程的ID,不会失败。

4. 比较两个线程的ID
~~~~~~~~~~~~~~~~~~~

int pthread_equal (pthread_t t1, pthread_t t2);

若参数t1和t2所标识的线程ID相等,则返回非零,否则返回0。

某些实现的pthread_t不是unsigned long int类型,
可能是结构体类型,无法通过“==”判断其相等性。

范例:equal.c

5. 终止线程
~~~~~~~~~~~

1) 从线程过程函数中return。

2) 调用pthread_exit函数。

void pthread_exit (void* retval);

retval - 和线程过程函数的返回值语义相同。

注意:在任何线程中调用exit函数都将终止整个进程。

范例:exit.c

6. 线程执行轨迹
~~~~~~~~~~~~~~~

1) 同步方式(非分离状态):
   创建线程之后调用pthread_join函数等待其终止,
   并释放线程资源。

2) 异步方式(分离状态):
   无需创建者等待,线程终止后自行释放资源。

int pthread_detach (pthread_t thread);

使thread参数所标识的线程进入分离(DETACHED)状态。
处于分离状态的线程终止后自动释放线程资源,
且不能被pthread_join函数等待。

成功返回0,失败返回错误码。

范例:detach.c

7. 取消线程
~~~~~~~~~~~

1) 向指定线程发送取消请求

int pthread_cancel (pthread_t thread);

成功返回0,失败返回错误码。

注意:该函数只是向线程发出取消请求,
并不等待线程终止。

缺省情况下,线程在收到取消请求以后,并不会立即终止,
而是仍继续运行,直到其达到某个取消点。在取消点处,
线程检查其自身是否已被取消了,并做出相应动作。

当线程调用一些特定函数时,取消点会出现。

2) 设置调用线程的可取消状态

int pthread_setcancelstate (int state,
    int* oldstate);

成功返回0,并通过oldstate参数输出原可取消状态
(若非NULL),失败返回错误码。

state取值:

PTHREAD_CANCEL_ENABLE  - 接受取消请求(缺省)。

PTHREAD_CANCEL_DISABLE - 忽略取消请求。

3) 设置调用线程的可取消类型

int pthread_setcanceltype (int type, int* oldtype);

成功返回0,并通过oldtype参数输出原可取消类型
(若非NULL),失败返回错误码。

type取值:

PTHREAD_CANCEL_DEFERRED     - 延迟取消(缺省)。

被取消线程在接收到取消请求之后并不立即响应,
而是一直等到执行了特定的函数(取消点)之后再响应该请求。

PTHREAD_CANCEL_ASYNCHRONOUS - 异步取消。

被取消线程可以在任意时间取消,
不是非得遇到取消点才能被取消。
但是操作系统并不能保证这一点。

范例:cancel.c

8. 线程属性
~~~~~~~~~~~

创建线程函数
int pthread_create (pthread_t* restrict thread,
    const pthread_attr_t* restrict attr,
    void* (*start_routine) (void*),
    void* restrict arg);
的第二个参数即为线程属性,传空指针表示使用缺省属性。

typedef struct
    // 分离状态
    //
    // PTHREAD_CREATE_DETACHED
    // - 分离线程。
    //
    // PTHREAD_CREATE_JOINABLE(缺省)
    // - 可汇合线程。
    //
    int detachstate;

    // 竞争范围
    //
    // PTHREAD_SCOPE_SYSTEM
    // - 在系统范围内竞争资源。
    //
    // PTHREAD_SCOPE_PROCESS(Linux不支持)
    // - 在进程范围内竞争资源。
    //
    int scope;

    // 继承特性
    //
    // PTHREAD_INHERIT_SCHED(缺省)
    // - 调度属性自创建者线程继承。
    //
    // PTHREAD_EXPLICIT_SCHED
    // - 调度属性由后面两个成员确定。
    //
    int inheritsched;

    // 调度策略
    //
    // SCHED_FIFO
    // - 先进先出策略。
    //
    // 没有时间片。
    //
    // 一个FIFO线程会持续运行,
    // 直到阻塞或有高优先级线程就绪。
    //
    // 当FIFO线程阻塞时,系统将其移出就绪队列,
    // 待其恢复时再加到同优先级就绪队列的末尾。
    //
    // 当FIFO线程被高优先级线程抢占时,
    // 它在就绪队列中的位置不变。
    // 因此一旦高优先级线程终止或阻塞,
    // 被抢占的FIFO线程将会立即继续运行。
    //
    // SCHED_RR
    // - 轮转策略。
    //
    // 给每个RR线程分配一个时间片,
    // 一但RR线程的时间片耗尽,
    // 系统即将移到就绪队列的末尾。
    //
    // SCHED_OTHER(缺省)
    // - 普通策略。
    //
    // 静态优先级为0。任何就绪的FIFO线程或RR线程,
    // 都会抢占此类线程。
    //
    int schedpolicy;

    // 调度参数
    //
    // struct sched_param
    //     int sched_priority; /* 静态优先级 */
    // ;
    //
    struct sched_param schedparam;

    // 栈尾警戒区大小(字节)
    //
    // 缺省一页(4096字节)。
    //
    size_t guardsize;

    // 栈地址
    //
    void* stackaddr;

    // 栈大小(字节)
    //
    size_t stacksize;
  pthread_attr_t;

不要手工读写该结构体,
而应调用pthread_attr_set/get函数设置/获取具体属性项。

1) 设置线程属性

第一步,初始化线程属性结构体

int pthread_attr_init (pthread_attr_t* attr);

第二步,设置具体线程属性项

int pthread_attr_setdetachstate (
    pthread_attr_t* attr,
    int detachstate);

int pthread_attr_setscope (
    pthread_attr_t* attr,
    int scope);

int pthread_attr_setinheritsched (
    pthread_attr_t* attr,
    int inheritsched);

int pthread_attr_setschedpolicy (
    pthread_attr_t* attr,
    int policy);

int pthread_attr_setschedparam (
    pthread_attr_t* attr,
    const struct sched_param* param);

int pthread_attr_setguardsize (
    pthread_attr_t* attr,
    size_t guardsize);

int pthread_attr_setstackaddr (
    pthread_attr_t* attr,
    void* stackaddr);

int pthread_attr_setstacksize (
    pthread_attr_t* attr,
    size_t stacksize);

int pthread_attr_setstack (
    pthread_attr_t* attr,
    void* stackaddr, size_t stacksize);

第三步,以设置好的线程属性结构体为参数创建线程

int pthread_create (pthread_t* restrict thread,
    const pthread_attr_t* testrict attr,
    void* (*start_routine) (void*),
    void* restrict arg);

第四步,销毁线程属性结构体

int pthread_attr_destroy (pthread_attr_t* attr);

2) 获取线程属性

第一步,获取线程属性结构体

int pthread_getattr_np (pthread_t thread,
    pthread_attr_t* attr);

第二步,获取具体线程属性项

int pthread_attr_getdetachstate (
    pthread_attr_t* attr,
    int* detachstate);

int pthread_attr_getscope (
    pthread_attr_t* attr,
    int* scope);

int pthread_attr_getinheritsched (
    pthread_attr_t* attr,
    int* inheritsched);

int pthread_attr_getschedpolicy (
    pthread_attr_t* attr,
    int* policy);

int pthread_attr_getschedparam (
    pthread_attr_t* attr,
    struct sched_param* param);

int pthread_attr_getguardsize (
    pthread_attr_t* attr,
    size_t* guardsize);

int pthread_attr_getstackaddr (
    pthread_attr_t* attr,
    void** stackaddr);

int pthread_attr_getstacksize (
    pthread_attr_t* attr,
    size_t* stacksize);

int pthread_attr_getstack (
    pthread_attr_t* attr,
    void** stackaddr, size_t* stacksize);

以上所有函数成功返回0,失败返回错误码。

范例:attr.c


来自为知笔记(Wiz)















































































































































第九课-09_02_磁盘及文件系统管理详解之五

一。创建文件系统---格式化分区就是创建文件系统mkfsmakefilesystem---创建文件系统的命令mkfs-tFSTYPEPARTmkfs-text2/dev/sda4---文件类型分区mkfs-text2=mkfs.ext2mkfs-tvfat=mkfs.vfat文件系统类型当前系统支持哪些文件系统--cat/proc/filesystems3.专门管理... 查看详情

第九课--09_03_磁盘及文件系统管理详解之六.avi(代码片段)

一。swap分区--交换分区--高性能磁盘,机械磁盘就放在靠外的磁道pageoutpageinfree--查看物理内存和交换空间的情况-m按M查看buffer缓冲---缓冲区,速度慢的设备遭到冲击--大河给小河送水--存放原数据cache缓存--包括缓冲的作用,作用... 查看详情

第九课:买卖点

http://www.52investing.com/jpkecheng/payRoom_31_109.html 查看详情

c第九课

【_(:з)∠)_咸_(:з)∠)_鱼_(:з)∠)_本_(:з)∠)_鱼_(:з)∠)_】 【知识梳理】    查看详情

重学java基础第九课:软件和软件关系

  查看详情

重学java基础第九课:软件和软件关系

  查看详情

第九课——框架窗口

一、关键点1.框架窗口分为两类:应用程序主框架窗口、文档窗口每个应用程序只能有一个主框架窗口对于单文档应用程序,主框架窗口就是文档窗口对于多文档应用程序,文档窗口是主框架窗口的子窗口 二、框架窗口初始... 查看详情

第二本书第九课

一.CSS3变形transform  1.平移:translate(x,y) translateX(x) translateY(y)      注意:如果想只向X轴平移那么可以translateX,如果想只向X轴平移那么可以translateY,X和Y不能共存。如果想向两个方向平移,那... 查看详情

scrapy学习第九课(代码片段)

python之Web应用框架Django学习第一课目标:搭建类似博客的简单页面实例:具体代码如下知识点:目标:搭建类似博客的简单页面实例:具体代码如下1、view.pyfromdjango.shortcutsimportrenderfromdjango.httpimportHttpResponsefrom... 查看详情

第九课聚集函数(代码片段)

9.1聚集函数1、avg()返回平均值函数,为了获得多个列的平均值,必须使用多个AVG()函数。AVG()函数忽略列值为NULL的行selectavg(prod_price),avg(prod_price*2)fromProductswherevend_id=‘DLL01‘2、count()函数注意:使用COUNT(*)对表中行的数目进行... 查看详情

第九课javascript语句运用实例

if...else语句1vara=12;2if(a>10){3alert("a的值大于10");4}else{5alert("a的值不大于10");6}if...else...if语句1vara=12;2if(a<10){3alert("a的值小于10");4}elseif(a==10){5alert("a的值等于10");6}else{7alert("a的值大于10");8}if嵌套 查看详情

第九课《标点上》

例句1:我们要善于夸奖别人。比如在年会上对同事说你的这个手饰和你的这个皮肤配合的特别好,使你看起来容光涣发。我们要善于夸奖别人。比如在年会上同事说你的这个手饰和你的这个皮肤配合的特别好,使你看起来容光... 查看详情

快学scala第九课(伴生对象和枚举)

Scala没有静态方法和静态字段,你可以用object这个语法结构来达到同样的目的。对象的构造器只有在第一次被使用时才调用。伴生对象apply方法:类和它的伴生对象可以互相访问私有特性,他们必须存在于同一个源文件。类中要... 查看详情

cv第九课不同网络的对比

1.ResNet+Inception有着最高的Accuracy2.VGG最占内存,最多的操作数3.GoogleNet最efficient,这里的效率怎么计算的?4.AlexNet最少的操作数,但内存大,accuracy低5.Resnet效率适中,accuracy最高 注:图中圆的面积代表所占内存大小,横坐标代... 查看详情

第九课计算器界面代码重构

一、重构的概念1、重构是以改善代码质量为目的代码重写(1)、使其软件的设计和架构更加合理(2)、提高软件的扩展性和维护性2、代码实现和代码重构的不同(1)、代码实现:按照设计编程实现,重在实现功能(2)、代码... 查看详情

python第九课类和对象(代码片段)

9.1类的定义以及创建对象类是对象的抽象,对象是类的具体。先有类才有对象,先对具体的事务进行分类,该事务才称为该分类的对象。类是由属性和行为组成,也就是变量和方法组成。#定义一个学生类classStudent... 查看详情

python第九课类和对象(代码片段)

9.1类的定义以及创建对象类是对象的抽象,对象是类的具体。先有类才有对象,先对具体的事务进行分类,该事务才称为该分类的对象。类是由属性和行为组成,也就是变量和方法组成。#定义一个学生类classStudent... 查看详情

第九课智能指针示例------狄泰软件学院

一、内存泄漏(臭名昭著的bug)(1)、动态申请堆空间,用完后不归还(2)、c++语言中没有垃圾回收机制(3)、指针无法控制所指向的堆空间生命周期(如局部指针生命周期结束了堆空间的生命周期还未结束)二、智能指针1... 查看详情