• Linux线程


    线程原语

    pthread_create
    创建线程

    #include <pthread.h>

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

                                    void *(*start_routine) (void *), void *arg);


    pthread_t *thread  传出参数,返回线程tid
    const pthread_attr_t *attr  设置线程属性,例如:线程用户栈大小,线程优先级
    void *(*start_routine) (void *) 线程的处理动作 函数指针void*fun(void*)
    void *arg 线程处理动作的参数

    pthread_self
    获取调用线程tid

    #include <pthread.h>

    pthread_t pthread_self(void);

      思考:pthread_self与tid等价吗?

          不等价,应为pthread_self在线程中获取tid(线程当前存在)

          tid是主控线程的变量在用户栈, 子线程看不到, 所以是主控线程获取输出的   输出时可能值一致(但可能此线程已经被释放,回收)

      如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函数return之前延时1秒,这只是一种权宜之计。

    pthread_exit

    线程退出函数

    调用线程退出函数,注意和exit函数的区别,任何线程里exit导致进程退出,其他线程未工作结束,主控线程退出时不能return或exit。
    需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

    #include <pthread.h>

    void pthread_exit(void *retval);

    void *retval:线程退出时传递出的参数,可以是退出值或地址,如是地址时,不能是线程内部申请的局部地址。

    pthread_join

    等待线程退出函数
    #include <pthread.h>

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

    pthread_t thread:回收线程的tid


    void **retval:接收退出线程传递出的返回值

    返回值:成功返回0,失败返回错误号

    调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

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

    如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。(即为-1)

    如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。

    如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。

    pthread_cancel
    在进程内某个线程可以取消另一个线程。

    #include <pthread.h>

    int pthread_cancel(pthread_t thread);


    被取消的线程,退出值,定义在Linux的pthread库中常数PTHREAD_CANCELED的值是-1。可以在头文件pthread.h中找到它的定义:
    #define PTHREAD_CANCELED ((void *) -1)

    实例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    void *thr_fn1(void *arg)
    {
      printf("thread 1 returning
    ");
      return (void *)1;
    }
    void *thr_fn2(void *arg)
    {
      printf("thread 2 exiting
    ");
      pthread_exit((void *)2);
    }
    void *thr_fn3(void *arg)
    {
      while(1) {
        printf("thread 3 writing
    ");
        sleep(1);
      }
    }
    int main(void)
    {
      pthread_t tid;
      void *tret;
      pthread_create(&tid, NULL, thr_fn1, NULL);
      pthread_join(tid, &tret);
      printf("thread 1 exit code %d
    ", (int)tret);
      pthread_create(&tid, NULL, thr_fn2, NULL);
      pthread_join(tid, &tret);
      printf("thread 2 exit code %d
    ", (int)tret);
      pthread_create(&tid, NULL, thr_fn3, NULL);
      sleep(3);
      pthread_cancel(tid);
      pthread_join(tid, &tret);
      printf("thread 3 exit code %d
    ", (int)tret);
      return 0;
    }

    结果:退出码分别是1,2,-1.

    对于线程3:如果线程中只有while(1),那么pthread_cancle则不能终止线程。如果加了sleep(1);则可以终止线程。

    总结:signal何时终止信号?(中断,异常,系统调用)

         cancle何时事件何时处理?(系统调用)

       就是说线程没有系统调用则不能使用cancle结束,所以我们可以用pthread_testcancle()来产生一次空调用,该函数的作用就是,产生一次系统调用检测终止事件。

    线程终止方式

    如果需要只终止某个线程而不终止整个进程,可以有三种方法:

    1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

    2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

    3.线程可以调用pthread_exit终止自己。

    同一进程的线程间,pthread_cancel向另一线程发终止信号。系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,才会真正结束线程。或调用pthread_testcancel,让内核去检测是否需要取消当前线程.

    pthread_detach

    将线程置为分离态
    #include <pthread.h>
    int pthread_detach(pthread_t tid);


    pthread_t tid:分离线程tid

    返回值:成功返回0,失败返回错误号。

    一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。同理,如果对一个已经处于join回收状态的线程调用depatch设置分离,也会失败。

    线程属性

    线程属性初始化

    先初始化线程属性,再pthread_create创建线程

    #include <pthread.h>
    int pthread_attr_init(pthread_attr_t *attr); //初始化线程属性
    int pthread_attr_destroy(pthread_attr_t *attr); //销毁线程属性所占用的资源

    线程分离状态的函数:

    #include <pthread.h>
    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
    //设置线程属性,分离or非分离

    int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); //获取程属性,分离or非分离

    pthread_attr_t *attr:被已初始化的线程属性

    int *detachstate:可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD _CREATE_JOINABLE(非分离线程)这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。

    要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

    NPTL


    1.察看当前pthread库版本
    getconf GNU_LIBPTHREAD_VERSION

    2.NPTL实现机制(POSIX),Native POSIX Thread Library

    3.使用线程库时gcc指定-lpthread

    细节注意
    1.主线程退出其他线程不退出,主线程应调用ptrhed_exit
    2.避免僵线程
    pthread_join
    pthread_deatch
    pthread_create指定分离属性 被join线程 可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;
    3.malloc和mmap申请的内存可以被其他线程释放
    4.如果线程终止时没有释放加锁的互斥量,则该互斥量不能再被使用
    5.应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
    6.信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制

  • 相关阅读:
    我理解的优秀软件工程师
    Hello 博客园!
    线程安全与可重入函数之间的区别与联系
    linux-粘滞位的使用
    死锁产生的四个必要条件及处理死锁的策略
    数据结构—位图
    Linux下进度条的简单实现
    Linux-find命令
    Linux文件3个时间点(access time,modify time,change time)
    各种排序算法的实现、总结
  • 原文地址:https://www.cnblogs.com/Lune-Qiu/p/9384765.html
Copyright © 2020-2023  润新知