11月10日笔记并发6_线程(线程函数,线程创建,退出,属性,互斥锁,条件变量)

typedef void*(*start_routine_t)(void*);

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

void pthread_exit(void *retval);

int pthread_setcancelstate(int state, int *oldstate);

int pthread_cancel(pthread_t thread);

int pthread_detach(pthread_t thread);

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

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                        const pthread_mutexattr_t *restrict attr);

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
                        const struct timespec *restrict abstime);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

int pthread_mutex_destroy(pthread_mutex_t *mutex);

int pthread_cond_init(pthread_cond_t *restrict cond,
                    const pthread_condattr_t *restrict attr);

int pthread_cond_wait(pthread_cond_t *restrict cond,
                    pthread_mutex_t *restrict mutex);

int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_destroy(pthread_cond_t *cond);

                                                                                                                                                           


1.问题的引入
    前面讲到 为了并发的执行任务,现代操作系统引入了进程的概念

    分析了创建一个子进程的流程

    (1)创建一个进程的开销很大 why?
        需要copy整个父进程的地址空间
    (2)进程间通信 需要用到第三方(如内核)
        进程间通信的开销也很大 why?
        进程间的地址空间是独立,访问内核时很消耗资源
        需要频繁的copy

    于是 就有人提出一个问题 能不能在同一个进程内部实现任务的并发执行?

    线程/轻量化的进程


2.线程 thread
    线程是比进程更小的活动单位,他是进程的一个执行路径(执行分支)
    线程也是并发的一种情形

    进程内部可以有多个线程 ,它们并发执行,

        单进程内部所有的线程共享整个地址空间

    main函数是进程的主线程
        进程的指令部分分成了多个线程去并发的运行

    线程的特点:
    (1)创建一个线程比创建一个进程的开销小的多 why
        因为创建一个线程 不需要拷贝进程地址空间;
    (2)实现线程间的通信十分方便 why
        因为一个进程内部所有的线程共享地址空间

    (3)线程也是一个动态概念
        进程(线程)状态图
        ready
        running 
        waitting 
    
    (4) 自从有了线程之后
        系统的调度的最小单位是线程
        系统分配资源的最小单位是进程


            线程是进程内一个指令的执行分支,多个线程线程就是多个指令序列并发执行
    指令必须在函数内部,线程的指令部分肯定会封装在一个函数的内部
    这个函数我们称为 “线程函数”,一个线程创建后,要执行的指令全都在该函数中
    这个函数执行完毕 线程的任务也就结束了。

        线程函数:

    typedef void*(*start_routine_t)(void*);

    线程函数:
        返回值 可能是char int double* struct 。。。。
        这么多类型,难道每个返回值都写一个对应的线程函数
         void*!

         参数为一个void*指针
         返回值为一个void *指针        

        如
            void* myThread(void* arg)
            {
                
            }

    thread实现的方式有很多中 比较常用的是POSIX 线程
        编译时 包含这个参数 -pthread 表示需要调用线程库

        比如:gcc main.c -pthread


3.linux下pthread的函数接口
    (1) 创建一个线程:
            线程有一个线程 id(tid,thread ID),类似于进程的pid
            用来表示一个线程的,在pthread中 用类型 pthread_t
            来描述一个线程的ID

            线程属性
                线程栈空间大小; 存放局部变量的
                线程的优先级;
                ......

            在pthread中,线程属性用结构体 pthread_attr_t 来描述
            开发者提供几个用于改变线程属性的函数接口,不建议
            程序员直接修改这个 pthread_attr_t 结构 
            用默认属性就好了

            NAME
                pthread_create - create a new thread

            SYNOPSIS
                #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
                    失败返回-1 同时errno被设置

                Compile and link with -pthread.   

(2)线程的退出
        (1) 线程函数跑完了
        (2) 在线程执行的任意时刻调用 pthread_exitm 退出线程  [中间直接退出]

            NAME
                pthread_exit - terminate calling thread

            SYNOPSIS
                #include <pthread.h>

                void pthread_exit(void *retval);

              void *retval:线程函数的返回值
        (3) 线程被别人给干掉了
            线程被别人取消了 (其他的线程调用 pthread_cancel(...))

            t1:pthread_cancel(t2)
                t2是不是一定会被干掉了?不一定
                这得看t2线程的属性中表明该线程是否可以被取消

                这个可以被取消的属性 pthread提供了一个函数接口(API),去修改它;                

                NAME
                    pthread_setcancelstate,  pthread_setcanceltype  - set cancelability state and
                    type

                SYNOPSIS
                    #include <pthread.h>

                    int pthread_setcancelstate(int state, int *oldstate);

                int state: 要设置的取消属性
                            用两个宏表示
                            PTHREAD_CANCEL_ENABLE:该线程可以被取消
                            PTHREAD_CANCEL_DISABLE:该线程不可被取消了

                                默认可以被取消
              int *oldstate: 上一次的线程的取消状态的属性

比如:

        在线程函数里面写:

        int *oldstate;

        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,oldstate);

                        返回值
                            成功返回0  失败返回非零的数

                那怎么去取消线程呢?

                NAME
                    pthread_cancel - send a cancellation request to a thread

                SYNOPSIS
                    #include <pthread.h>

                    int pthread_cancel(pthread_t thread);

            pthread_t thread:你要取消的线程ID

                    成功返回0 失败返回非零的数

                (是否取消成功和返回值没有关系,返回值只证明是否运行了此函数)
 


    一个线程退出了,并不代表它所有的资源都被释放
    一个进程退出了,它的资源是否被释放,取决于一个属性
        deatech;分离属性
            ENABLE:分离
                该线程结束后,由系统托管,他的资源会全部自动释放
                分离之后就没有办法变成非分离
            DISABLE:非分离  默认属性
                该线程结束后,它会有部分资源不会被自动释放,
                需要其他线程调用pthread_join函数才能完全释放

            SYNOPSIS
                #include <pthread.h>

                int pthread_detach(pthread_t thread);

               pthread_t thread:要分离线程的ID 
                    
                    返回值;
                        成功返回0 失败返回一个错误码(出错了再去百度含义)


        (3)等待一个线程退出
            上面讲到非分离状态下,线程结束后,有部分资源不会被自动释放
            那么怎么办?
            pthread_join

            pthread_join:用来等待一个指定的线程退出,然后释放剩余资源;
                        请注意,如果等待的线程是detach属性,这个线程
                        由系统托管了,pthread_join直接返回,不会等待。

            NAME
                pthread_join - join with a terminated thread

            SYNOPSIS
                #include <pthread.h>

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

      pthread_t thread:要等待那个线程  如果等待的那个线程已经是detach属性
                            那么这个函数一点用处都没有,因为此时被detach属性
                            的线程被系统托管了,压根不会等待。
               不要对同一个线程调用 pthread_join 和 pthread_detach 
    void **retval:指向的空间用于保存线程函数的返回值(void *)
                    

    (4)线程间的同步和互斥
        为了线程间,有序地访问共享资源,也要引入“信号量的机制”
        (4.1)信号量(System V sem/POSIX sem)
        
        (4.2) 线程互斥锁
            线程互斥锁也是一个信号量,只不过线程互斥锁,
            存在进程的地址空间,只能用于该进程的所有线程间的同步和互斥,
            线程互斥锁效率要比信号量要高

虚拟机里面终端安装命令

            //安装线程互斥锁的说明书
            sudo apt-get install manpages-posix-dev

            (1)初始化一个线程互斥锁

                NAME
                    pthread_mutex_init — destroy and initialize a mutex

                SYNOPSIS
                    #include <pthread.h>

                    int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                        const pthread_mutexattr_t *restrict attr);

                            mutex: 待初始化的线程互斥锁的指针
                            attr: 线程互斥锁属性,一般为NULL,采用默认的属性
                                    默认初始化后的值为 1 unlock

                            返回值 成功返回0 失败返回非零的值

                    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

            (2)P/V操作
                pthread_mutex_lock               "阻塞等待"  一直等待,直到成功上锁
                pthread_mutex_trylock           "非阻塞等待" 尝试一次获取 获取不了就走
                pthread_mutex_timedlock      "限时等待"

                SYNOPSIS
                    #include <pthread.h>

                    int pthread_mutex_lock(pthread_mutex_t *mutex);

                    int pthread_mutex_trylock(pthread_mutex_t *mutex);

                        这个两个函数的参数是一样的 mutex 都是表示对那把锁进行操作
                        pthread_mutex_lock  表示“死等” 如果锁不可用,则一直等待
                        该锁可以用未知或者被信号打断
                        pthread_mutex_trylock 表示能获取则获取 不能获取则立马返回

                SYNOPSIS
                    #include <pthread.h>
                    #include <time.h>

                    int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
                        const struct timespec *restrict abstime);

                        mutex 表示对那把锁进行操作
                        abstime:绝对时间  如果想要等待1秒中
                            那么就要 获取当前的时间 + 要等待的时间

                        [ 绝对时间 = 当前时间 + 等待时间 ]
                            这个结构体具体怎么用 看POSIX信号量对应P操作


                    三个P操作的函数的返回值
                            成功返回0 表示获取到了这把互斥锁 -》可以进入到临界区执行了
                            失败返回非零的值 表示没有获取到这把互斥锁 -》不可以进入临界区执行

                V操作 解锁

                int pthread_mutex_unlock(pthread_mutex_t *mutex);

                    mutex 要对那一把锁进行解锁
                    返回值
                        成功返回0
                        失败返回非零的值 

          (3)销毁操作

                SYNOPSIS
                    #include <pthread.h>

                    int pthread_mutex_destroy(pthread_mutex_t *mutex);

                        mutex:哪一个线程互斥锁不要了 需要销毁呢?

    (5)生产者-消费者模型
        1.共享资源互斥访问的问题
            资源生成和消耗速度不匹配
        2.消费者怎么知道缓冲区中的有没有数据?
            a.轮询 不断的询问 看有没有数据
                轮询的缺陷
                    1.浪费CPU
                    2.有时间差 不及时
                    3.占用总线
            b.让出CPU(sleep) 当CPU有数据的时候 再叫我(唤醒)
                线程的条件变量 同步

    线程的条件变量到底是一个什么东西呢?
        在多线程程序设计中,我们可以用“条件变量”来表示
        一个特定的事件或条件

        由于这个条件变量是由程序员来定义或解释的
        到底表示什么条件/事件完全由程序员来决定

        条件变量一共由几个操作?
        四个

        初始化一个条件变量
        等待一个条件变量(等待的过程中 让出CPU的使用权 直到条件满足被唤醒)
        唤醒一个条件变量(条件满足吗,唤醒正在等待的该条件变量的线程)
        销毁一个条件变量

        初始化
        t2线程
        if(条件不满足/事件没产生)
        {
            sleep(休眠  等待被唤醒);
        }

        t1 
        if(条件满足了 事件产生了)
        {
            wakeUp(叫醒等待的线程)
        }        ------------------------
        条件变量的API
        (1)初始化一个条件变量

            SYNOPSIS
                #include <pthread.h>

                int pthread_cond_init(pthread_cond_t *restrict cond,
                    const pthread_condattr_t *restrict attr);

                   cond:要初始化的条件变量
                        pthread_cond_t cond;
                        &cond
                    attr:条件变量的属性 一般为NULL,表示采用一个默认属性
                    返回值
                        成功返回0
                        失败返回非零的值
        (2)等待一个条件变量

            SYNOPSIS
                #include <pthread.h>

                int pthread_cond_wait(pthread_cond_t *restrict cond,
                    pthread_mutex_t *restrict mutex);

                    cond: 等待那个条件变量  
                    mutex:线程互斥锁 为了保护cond所代表事件或共享资源的
                        在调用这个函数的时候 meteu是初始locked一个状态
                                这难道不会有问题吗?
                                    不会
                                pthread_cond_wait()
                                {
                                    //mutex locker
                                    ......
                                    Unlocked
                                    让出CPU

                                    ......

                                    当条件(条件变量和互斥解锁)满足 其他生产者唤醒你的时候
                                    Lock Mutex;
                                }

                                把条件变量和互斥锁称为一对mc 
                                表明这两个东西是成对出现的

            int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                    pthread_mutex_t *restrict mutex,
                    const struct timespec *restrict abstime);

                    abstime:绝对时间  表示要超时了 不论条件变量
                            是否被唤醒 都继续往下执行 不睡了
        (3)唤醒一个条件变量:唤醒正在等待条件变量cond的线程

            SYNOPSIS
                #include <pthread.h>
                //唤醒所有等待cond条件变量的线程
                int pthread_cond_broadcast(pthread_cond_t *cond);

                int pthread_cond_signal(pthread_cond_t *cond);

                //只唤醒一个等待cond的线程
      (4)销毁一个条件变量

            SYNOPSIS
                #include <pthread.h>

                int pthread_cond_destroy(pthread_cond_t *cond);


                        你要销毁那个条件变量
                    返回值
                        成功返回0
                        失败返回非零的值



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值