• 12.2 linux下的线程


    什么是线程:

      在一个程序里的一个执行路线就叫做线程(thread),更准确的定义是:线程是“一个进程内部的控制序列”

      一切进程至少都有一个执行线程

    进程与线程:

      进程是资源竞争的基本单位

      线程是程序执行的最小单位

      线程会使用进程的全局变量

      线程共享进程数据,但也拥有自己的一部分数据

        线程ID

        程序计数器

        寄存器组

        栈

        errno

      一个进程内部的线程可以共享资源

        代码段

        数据段

        打开文件和信号

    单线程和多线程模型如下:

     如何从进程往线程中传数据?又如何从线程中将数据传出来呢?

    1、使用全局变量,可以将数据传给线程

    2、在进程中分配内存,通过pthread_create的第四个参数传给线程

    fork和创建新线程的区别:

    线程的属性是可以修改的。竞争范围可以修改。

    POSIX线程库:

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

      要使用这些函数库,需要引入头文件<pthread.h>

      链接这些线程函数库时要使用编译器命令的“-lpthread”选项

    pthread_create函数:

      原型:int  pthread_create(pthread_t  *thread,  const  pthread_attr_t  *attr,  void *(*start_routine)(void *),  void  *arg)

      功能:创建一个新的线程

      参数:

        thread:返回的线程ID

        attr:设置线程的属性,attr为NULL表示使用默认属性

        start_routine:是一个函数地址,线程启动后要执行的函数

        arg:传给线程启动函数的参数

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

    错误检查:

      传统的一些函数是成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误

      pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做),而是将错误码通过返回值返回

      pthread同样也提供了线程内的errno变量,以支持其他使用errno的代码,对于pthreads函数的错误,建议通过返回值来判定,因为读取返回值

      要比读取线程内的errno变量的开销更小。

    其他线程函数:

     

    进程和线程对比:

    进程:

      fork;有pid;有pcb控制块;有僵尸进程;

    线程:

      pthread_create;有tid;有tcb控制块;有僵尸线程

    线程和进程有一个巨大的区别,就是线程依赖进程,如果进程死了,线程也会死掉。而进程是父进程死了,子进程依旧可以运行。因为进程有自己独立的内存空间。总之,线程依赖于进程的生命周期。

    线程示例程序如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 void *start_routine(void *arg)
    16 {
    17     int i = 0;
    18     printf("I am thread. 
    ");
    19     
    20     for(i = 0; i < 10; i++)
    21     printf("B");
    22     fflush(stdout);
    23     
    24     return NULL;
    25 }
    26 
    27 int main()
    28 {
    29     pthread_t thread;
    30     int i = 0;
    31     
    32     pthread_create(&thread, NULL, start_routine, NULL);
    33     
    34     for(i = 0; i< 10; i++)
    35     {
    36         printf("A");
    37         fflush(stdout);
    38     }
    39     
    40     return 0;
    41 }

    执行完32行,线程就去执行线程体函数了,34行开始的for循环已经不再属于线程,也就是说线程执行完线程体就返回了,不会执行到34行。

    运行结果如下:

    我们看到只打印出了AAAAAAAAA,这是因为,进程(主线程)死了之后,线程也就死了,它还没有来得及执行线程体就死了。我们给进程下面加上sleep,如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 void *start_routine(void *arg)
    16 {
    17     int i = 0;
    18     printf("I am thread. 
    ");
    19     
    20     for(i = 0; i < 10; i++)
    21     printf("B");
    22     fflush(stdout);
    23     
    24     return NULL;
    25 }
    26 
    27 int main()
    28 {
    29     pthread_t thread;
    30     int i = 0;
    31     
    32     pthread_create(&thread, NULL, start_routine, NULL);
    33     
    34     for(i = 0; i< 10; i++)
    35     {
    36         printf("A");
    37         fflush(stdout);
    38     }
    39     
    40     sleep(2);
    41     
    42     return 0;
    43 }

    结果如下:

    我们看到线程成功打印出了数据。

     打印全局变量,程序如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 int g_num = 0;
    16 
    17 void *start_routine(void *arg)
    18 {
    19     int i = 0;
    20     printf("I am thread. 
    ");
    21     printf("g_num = %d
    ", g_num);
    22     
    23     for(i = 0; i < 10; i++)
    24     {
    25         printf("B");
    26         fflush(stdout);
    27     }
    28     
    29     printf("
    ");
    30     
    31     return NULL;
    32 }
    33 
    34 int main()
    35 {
    36     pthread_t thread;
    37     int i = 0;
    38     
    39     g_num = 10;
    40     
    41     pthread_create(&thread, NULL, start_routine, NULL);
    42     
    43     for(i = 0; i< 10; i++)
    44     {
    45         printf("A");
    46         fflush(stdout);
    47     }
    48     printf("
    ");
    49     sleep(2);
    50     
    51     return 0;
    52 }

    执行结果如下:

    多进程打印全局变量程序如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 int g_num = 0;
    16 
    17 void *start_routine(void *arg)
    18 {
    19     int i = 0;
    20     printf("I am thread. 
    ");
    21     printf("g_num = %d
    ", g_num);
    22     
    23     for(i = 0; i < 10; i++)
    24     {
    25         printf("B");
    26         fflush(stdout);
    27     }
    28     
    29     printf("
    ");
    30     
    31     return NULL;
    32 }
    33 
    34 int main()
    35 {
    36     pid_t pid;
    37     int i = 0;
    38     
    39     g_num = 10;
    40     
    41     pid = fork();
    42     
    43     if( pid == 0)
    44     {
    45         printf("I am child 
    ");
    46         printf("g_num = %d
    ", g_num);
    47         for(i = 0; i< 10; i++)
    48         {
    49             printf("B");
    50             fflush(stdout);
    51         }
    52         printf("
    ");
    53         exit(0);
    54     }
    55     
    56     for(i = 0; i< 10; i++)
    57     {
    58         printf("A");
    59         fflush(stdout);
    60     }
    61     printf("
    ");
    62     sleep(2);
    63     
    64     return 0;
    65 }

    结果如下:

     获取线程ID的示例:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 int g_num = 0;
    16 
    17 void *start_routine(void *arg)
    18 {
    19     int i = 0;
    20     printf("I am thread. 
    ");
    21     printf("g_num = %d
    ", g_num);
    22     printf("thread id = %lu
    ", pthread_self());
    23     
    24     for(i = 0; i < 10; i++)
    25     {
    26         printf("B");
    27         fflush(stdout);
    28     }
    29     
    30     printf("
    ");
    31     
    32     return NULL;
    33 }
    34 
    35 int main()
    36 {
    37     pthread_t thread;
    38     int i = 0;
    39     
    40     g_num = 10;
    41     
    42     pthread_create(&thread, NULL, start_routine, NULL);
    43     
    44     for(i = 0; i< 10; i++)
    45     {
    46         printf("A");
    47         fflush(stdout);
    48     }
    49     printf("
    ");
    50     sleep(2);
    51     
    52     return 0;
    53 }

    结果如下:

     上面的程序中,我们使用sleep等待,让子进程先运行,但是这不是根本的解决办法,我们使用pthread_join等待子线程退出,程序如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 int g_num = 0;
    16 
    17 void *start_routine(void *arg)
    18 {
    19     int i = 0;
    20     printf("I am thread. 
    ");
    21     printf("g_num = %d
    ", g_num);
    22     printf("thread id = %lu
    ", pthread_self());
    23     
    24     for(i = 0; i < 10; i++)
    25     {
    26         printf("B");
    27         fflush(stdout);
    28     }
    29     
    30     sleep(3);
    31     
    32     printf("
    ");
    33     
    34     return NULL;
    35 }
    36 
    37 int main()
    38 {
    39     pthread_t thread;
    40     int i = 0;
    41     
    42     g_num = 10;
    43     
    44     pthread_create(&thread, NULL, start_routine, NULL);
    45     
    46     for(i = 0; i< 10; i++)
    47     {
    48         printf("A");
    49         fflush(stdout);
    50     }
    51     printf("
    ");
    52     
    53     pthread_join(thread, NULL);
    54     
    55     return 0;
    56 }

    结果如下:

    线程有两种死掉的方法,一个是自杀一个是他杀。

    我们先用exit使子线程退出,程序如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 int g_num = 0;
    16 
    17 void *start_routine(void *arg)
    18 {
    19     int i = 0;
    20     printf("I am thread. 
    ");
    21     printf("g_num = %d
    ", g_num);
    22     printf("thread id = %lu
    ", pthread_self());
    23     
    24     for(i = 0; i < 10; i++)
    25     {
    26         printf("B");
    27         fflush(stdout);
    28     }
    29     
    30     sleep(3);
    31     
    32     printf("
    ");
    33     
    34     exit(0);
    35 }
    36 
    37 int main()
    38 {
    39     pthread_t thread;
    40     int i = 0;
    41     
    42     g_num = 10;
    43     
    44     pthread_create(&thread, NULL, start_routine, NULL);
    45     
    46     for(i = 0; i< 10; i++)
    47     {
    48         printf("A");
    49         fflush(stdout);
    50     }
    51     printf("
    ");
    52     
    53     pthread_join(thread, NULL);
    54     printf("child thread die
    ");
    55     
    56     return 0;
    57 }

    结果如下:

    我们发现子线程退出后,父进程没有打印出第54行的数据,说明子线程用exit退出后,父进程也挂了,相当于同归于尽。因此,线程退出千万不能用exit。

     我们可以使用pthread_exit让线程死掉,程序如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 int g_num = 0;
    16 
    17 void *start_routine(void *arg)
    18 {
    19     int i = 0;
    20     printf("I am thread. 
    ");
    21     printf("g_num = %d
    ", g_num);
    22     printf("thread id = %lu
    ", pthread_self());
    23     
    24     for(i = 0; i < 10; i++)
    25     {
    26         printf("B");
    27         fflush(stdout);
    28     }
    29     
    30     sleep(3);
    31     
    32     printf("
    ");
    33     
    34     pthread_exit(NULL);
    35 }
    36 
    37 int main()
    38 {
    39     pthread_t thread;
    40     int i = 0;
    41     
    42     g_num = 10;
    43     
    44     pthread_create(&thread, NULL, start_routine, NULL);
    45     
    46     for(i = 0; i< 10; i++)
    47     {
    48         printf("A");
    49         fflush(stdout);
    50     }
    51     printf("
    ");
    52     
    53     pthread_join(thread, NULL);
    54     printf("child thread die
    ");
    55     
    56     return 0;
    57 }

    执行结果如下:

    可以看到第54行打印出来了。

    线程他杀就是父进程调用pthread_cancel杀掉子线程,这个很少用。

    父进程如果不想等待线程结束,可以让线程脱离这个进程运行,使用pthread_detach函数,程序如下:

     1 #include <unistd.h>
     2 #include <sys/types.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <errno.h>
     7 #include <string.h>
     8 #include <pthread.h>
     9 /*
    10 int pthread_create(pthread_t *restrict thread,
    11            const pthread_attr_t *restrict attr,
    12            void *(*start_routine)(void*), void *restrict arg);
    13 */
    14 
    15 int g_num = 0;
    16 
    17 void *start_routine(void *arg)
    18 {
    19     int i = 0;
    20     pthread_detach(pthread_self());
    21     printf("I am thread. 
    ");
    22     printf("g_num = %d
    ", g_num);
    23     printf("thread id = %lu
    ", pthread_self());
    24     
    25     for(i = 0; i < 10; i++)
    26     {
    27         printf("B");
    28         fflush(stdout);
    29     }
    30     
    31     sleep(3);
    32     
    33     printf("
    ");
    34     
    35     pthread_exit(NULL);
    36 }
    37 
    38 int main()
    39 {
    40     pthread_t thread;
    41     int i = 0;
    42     
    43     g_num = 10;
    44     
    45     pthread_create(&thread, NULL, start_routine, NULL);
    46     
    47     for(i = 0; i< 10; i++)
    48     {
    49         printf("A");
    50         fflush(stdout);
    51     }
    52     printf("
    ");
    53     
    54     pthread_join(thread, NULL);
    55     printf("child thread die
    ");
    56     
    57     return 0;
    58 }

    结果如下:

    一般在线程体函数中立即调用pthread_detach函数。

    我们编译多线程程序时要加上 -lpthread,使用ldd可以查看程序用了哪些动态库,如下所示:

     可以使用nm查看一个库中的函数(符号),如下:

  • 相关阅读:
    Docker(二)Image 与网络
    Docker(一)概念与基础
    Apache Hudi 介绍与应用
    Flink读写Kafka
    Flink 应用的一致性保障
    Flink系统配置
    Flink架构(五)- 检查点,保存点,与状态恢复
    Flink架构(四)- 状态管理
    HBase 中读 HDFS 调优
    Nginx模块之http.md
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9451732.html
Copyright © 2020-2023  润新知