什么是线程:
在一个程序里的一个执行路线就叫做线程(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查看一个库中的函数(符号),如下: