当前位置:   article > 正文

【Linux应用】线程简介以及如何创建线程_pthread_create时间

pthread_create时间

1.进程和线程诞生记

我们的电脑主要由CPU+RAM+各种资源(比如显卡,光驱,键盘,GPS等外设)构成,但是电脑的运行实际就是CPU和相关寄存器以及RAM之间的事情。
CPU运行太快,难以想象的快,寄存器仅仅能够追的上他的脚步,RAM和别的挂在各总线上的设备则难以望其项背。那当多个任务要执行的时候怎么办呢?轮流着来?或者谁优先级高谁来?不管怎么样的策略,一句话就是在CPU看来就是轮流着来。而且因为速度差异,CPU实际的执行时间和等待执行的时间是数量级的差异。比如工作1秒钟,休息一个月。所以多个任务,轮流着来,让CPU不那么无聊,给流逝的时间增加再多一点点的意义。这些任务,在外在表现上就仿佛是同时在执行。
执行一段程序代码,实现一个功能的过程之前 ,当得到CPU的时候,相关的资源必须也已经就位,就是万事俱备只欠CPU这个东风。所有这些任务都处于就绪队列,然后由操作系统的调度算法,选出某个任务,让CPU来执行。然后就是PC指针指向该任务的代码开始,由CPU开始取指令,然后执行。
在这里插入图片描述
除了CPU以外所有的执行环境,主要是寄存器的一些内容,就构成了的进程的上下文环境。进程的上下文是进程执行的环境。当这个程序执行完了,或者分配给他的CPU时间片用完了,那它就要被切换出去,等待下一次CPU的临幸。在被切换出去做的主要工作就是保存程序上下文,因为这个是下次他被CPU临幸的运行环境,必须保存。
串联起来的事实:前面讲过在CPU看来所有的任务都是一个一个的轮流执行的,具体的轮流方法就是:先加载进程A的上下文,然后开始执行A,保存进程A的上下文,调入下一个要执行的进程B的进程上下文,然后开始执行B,保存进程B的上下文。
进程就是上下文切换之间的程序执行的部分。是运行中的程序的描述,也是对应于该段CPU执行时间的描述。在软件编码方面,我们说的进程,其实是稍不同的,编程语言中创建的进程是一个无限loop,对应的是tcb块。这个是操作系统进行调度的单位。所以和上面的cpu执行时间段还是不同的。进程,与之相关的东东有寻址空间,寄存器组,堆栈空间等。即不同的进程,这些东东都不同,从而能相互区别。
进程的颗粒度太大,每次的执行都要进行进程上下文的切换。如果我们把进程比喻为一个运行在电脑上的软件,那么一个软件的执行不可能是一条逻辑执行的,必定有多个分支和多个程序段,就好比要实现程序A,实际分成 a,b,c等多个块组合而成。那么这里具体的执行就可能变成:程序A得到CPU =》CPU加载上下文,开始执行程序A的a小段,然后执行A的b小段,然后再执行A的c小段,最后CPU保存A的上下文。这里a,b,c的执行是共享了A进程的上下文,CPU在执行的时候仅仅切换线程的上下文,而没有进行进程上下文切换的。进程的上下文切换的时间开销是远远大于线程上下文时间的开销。这样就让CPU的有效使用率得到提高。这里的a,b,c就是线程,也就是说线程是共享了进程的上下文环境,更为细小的CPU时间段。线程主要共享的是进程的地址空间。
进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同。注意这里描述的进程线程概念和实际代码中所说的进程线程是有区别的。编程语言中的定义方式仅仅是语言的实现方式,是对进程线程概念的物化。
做个简单的比喻:进程=火车,线程=车厢

    线程在进程下行进(单纯的车厢无法运行)
    一个进程可以包含多个线程(一辆火车可以有多个车厢)
    不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
    同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
    进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
    进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
    进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
    进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
    进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.线程简介

线程是计算机中独立运行的最小单位。每个线程占用的CPU时间是由系统分配的,因此可以把线程看成操作系统分配CPU时间的基本单位。每个线程只有在系统分配给它的时间片内才能取得CPU控制权,执行线程中的代码。
Linux操作系统在一个进程内生成多个线程。多线程和多进程相比,拥有以下优点:
(1)进程都有独立的地址空间,创建新进程要耗费时间为期分配系统资源,而线程共享进程的地址空间,所以创建线程花费的时间要少得多。
(2)系统调度方面,由于进程地址空间独立而线程共享地址空间,所以线程间的切换速度要远远快过进程间的切换速度。
(3)通信机制方面,进程间的数据空间相互独立,彼此通信要以专门的通信方式进行,且必须经过操作系统。而多线程共享共享数据空间,一个线程的数据可以直接提供给其他线程使用,而不必经过操作系统。因此,线程间的通信更加方便省时。
虽然线程在进程内部共享地址空间、打开的文件描述符等资源。但是线程也有其私有的数据信息,包括:
(1)线程ID:每个线程都由一个唯一的线程号。
(2)寄存器(包括程序计数器和堆栈指针)。
(3)堆栈
(4)信号掩码
(5)优先级
(6)线程私有的存储空间
在主线程里创建线程,程序就会在创建线程的地方产生分支,变成两个程序执行。这似乎和多进程一样,其实不然。子进程时通过拷贝父进程的地址空间来实现的;而线程与进程内的其他线程共享程序代码,一段代码可以同时被多个线程执行。
在这里插入图片描述

如何理解多线程

这么理解吧,假设你正在追一个女生。 某天晚上,你给她发了一个消息,过了一分钟之后她回了你一个表情包【微笑脸】。
你很开心,你觉得她正在跟你聊天。
你又给她发了一个消息,过了一会她又回复你消息了,你觉得自己追到她很有希望。
而事实是,在回复你消息之前,她分别给她正在追的男生发了三条消息,让后又给备胎1,备胎2,备胎3…
分别回复了一个表情包或一个“哦”,“嗯呢”,然后才轮到你。
而这些,你都是不知道的,其他人都是不知道的,所有的人都以为她只在和自己聊天,因为她回复消息很“及时”。只要她速度足够快,她能骗过每一个人。
这就是恋爱过程中的多线程,理解了吗?
至于计算机中的多线程,道理也是一样。因为计算机运算速度很快,而且采用的时间片技术和各种调度算法,可以保证每个程序都能得到执行,时延非常短,我们是感觉不出来的。比如几个程序在一秒钟之内就轮换执行了200多次,
我们只会以为每一个程序都是没有被中断过的,是单独一直在执行的。

3.pthread_create

线程的创建通过函数pthread_create来完成,声明如下:

  #include <pthread.h>
  
  int pthread_create(pthread_t *thread, pthread_attr_t * attr, void* (*start_routine)(void *),void *arg);
  • 1
  • 2
  • 3
  • hread: 是一个指针,线程创建成功时,用以返回创建的线程ID
  • attr:指定线程属性,NULL表示使用默认属性
  • start_routine:函数指针,指向线程创建后要调用的函数。这个被线程调用的函数也称为线程函数。
  • arg:该参数指向传递给线程函数的参数。
    线程创建成功时,pthread_create函数返回0,新创建的线程开始运行第3个参数所指向的函数,原来的线程继续运行。

4.代码

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

/* 声明结构体 */
struct member
{
    int num;
    char *name;
};

/* 定义线程pthread */
static void * pthread(void *arg)
{
    struct member *temp;

    /* 线程pthread开始运行 */
    printf("thread starts,ID is %ld\n",pthread_self());

    /* 令主线程继续执行 */
    sleep(5);

    /* 打印传入参数 */
    temp = (struct member *)arg;
    printf("*************printinfo in thread************\n");
    printf("member->num:%d\n",temp->num);
    printf("member->name:%s\n",temp->name);

    return NULL;
}

int main(int agrc,char* argv[])
{
    pthread_t tidp;
    struct member *pMember;

    /* 为结构体变量pMember赋值 */
    pMember = (struct member *)malloc(sizeof(struct member));
    pMember->num=1;
    pMember->name="Proctol Forest";

     printf("main starts,ID is %ld\n",pthread_self());
    /* 创建线程pthread */
    if ((pthread_create(&tidp, NULL, pthread, (void*)pMember)) == -1)
    {
        printf("create error!\n");
        return 1;
    }

    /* 令线程pthread先运行 */
    sleep(1);

    /* 线程pthread睡眠5s,此时main可以先执行 */
    printf("mian continue!\n");

    /* 等待线程pthread释放 */
    if (pthread_join(tidp, NULL))
    {
        printf("thread is not exit...\n");
        return -2;
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

5.编译

Linux下的多线程程序,需要使用头文件pthread.h,链接时需要使用库libpthread.a。

gcc pthread.c  -o pthread -lpthread
  • 1

6.结果

在这里插入图片描述

加入讨论

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/181984
推荐阅读
相关标签
  

闽ICP备14008679号