通过阅读《什么是线程》一节,我们了解了什么是线程以及什么是多线程。本节,我们教大家编写第一个多线程程序。

大多数操作系统都支持同时执行多个程序,包括常见的 Windows、Linux、Mac OS X 操作系统等。为了避免多个程序访问系统资源(包括文件资源、I/O 设备、网络等)时产生冲突,操作系统会将可能产生冲突的系统资源保护起来,阻止应用程序直接访问。如果程序中需要访问被操作系统保护起来的资源,需使用操作系统规定的方法(函数、命令),我们习惯将这些调用方法(函数、命令)称为接口(Application Programming Interface,简称 API)

§ 2.第一个多线程程序 - 图1

事实上,无论我们用哪种编程语言编写多线程程序,最终都要借助操作系统预留的接口实现。接下来,我们将为您讲解如何借助 Linux 系统预留的接口编写 C 语言多线程程序。

POSIX标准

类 UNIX 系统有很多种版本,包括 Linux、FreeBSD、OpenBSD 等,它们预留的系统调用接口各不相同。但幸运的是,几乎所有的类 UNIX 系统都兼容 POSIX 标准。

POSIX 标准全称“Portable Operating System Interface”,中文译为可移植操作系统接口,最后的字母 X 代指类 UNIX 操作系统。简单地理解,POSIX 标准发布的初衷就是为了统一所有类 UNIX 操作系统的接口,这意味着,只要我们编写的程序严格按照 POSIX 标准调用系统接口,它就可以在任何兼容 POSIX 标准的类 UNIX 系统上运行。

所谓兼容,很多支持 POSIX 标准的类 UNIX 操作系统并没有从根本上修改自己的 API,它们仅仅通过对现有的 API 进行再封装,生成了一套符合 POSIX 标准的系统接口,进而间接地支持 POSIX 标准

值得一提的是,POSIX 标准中规范了与多线程相关的系统接口。我们在 Linux 系统上编写多线程程序,只需在程序中引入<pthread.h>头文件,调用该文件中包含的函数即可实现多线程编程。

注意,pthread.h 头文件中只包含各个函数的声明部分,具体实现位于 libpthread.a 库中。

第一个多线程程序

分析如下程序 test.c:

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. // 定义线程1要执行的函数,arg为接收线程传递过来的数据
  4. void *Thread1(void *arg) {
  5. printf("Thread1 in action\n");
  6. return "Thread1 执行完毕";
  7. }
  8. // 定义线程2要执行的函数,arg为接收线程传递过来的数据
  9. void *Thread2(void* arg) {
  10. printf("Thread2 in action\n");
  11. return "Thread2 执行完毕";
  12. }
  13. // 主函数
  14. int main() {
  15. int res; // 返回值res为0表示线程创建成功,反之则创建失败
  16. pthread_t thread1, thread2; // 创建的线程thread1和thread2
  17. void *thread_result;
  18. res = pthread_create(&thread1, NULL, Thread1, NULL);
  19. if (res != 0) {
  20. printf("Faild to create thread1\n");
  21. return 0;
  22. }
  23. else {
  24. printf("Succeed to create thread1\n");
  25. }
  26. res = pthread_create(&thread2, NULL, Thread2, NULL);
  27. if (res != 0) {
  28. printf("Faild to create thread2\n");
  29. return 0;
  30. }
  31. else {
  32. printf("Succeed to create thread2\n");
  33. }
  34. res = pthread_join(thread1, &thread_result);
  35. printf("%s\n", (char*) thread_result);
  36. res = pthread_join(thread2, &thread_result);
  37. printf("%s\n", (char*) thread_result);
  38. printf("主线程执行完毕\n");
  39. return 0;
  40. }

执行该程序:

  1. [root@iZbp18vd1p2tytbwn5vgaqZ CPlusPlus]# gcc test.c -o test -lpthread
  2. [root@iZbp18vd1p2tytbwn5vgaqZ CPlusPlus]# ./test
  3. Succeed to create thread1
  4. Succeed to create thread2
  5. Thread2 in action
  6. Thread1 in action
  7. Thread1 执行完毕
  8. Thread2 执行完毕
  9. 主线程执行完毕

注意编译过程中必须包含 -lpthread 参数,否则会导致程序链接失败。

程序中共存在 3 个线程,包括本就存在的主线程以及两个调用 pthread_create() 函数创建的线程(又称子线程),其中名为 thread1 的线程负责执行 thread1() 函数,名为 thread2 的线程负责执行 thread2() 函数。

程序中调用了两次 pthread_join() 函数,第 40 行 pthread_join() 函数的功能是令主线程等待 thread1 线程执行完毕后再执行后续的代码,第 51 行处pthread_join() 函数的功能是令主线程等待 thread2 线程执行完毕后在执行后续的代码。

§ 2.第一个多线程程序 - 图2

由此,我们已经学会了如何编写一个简单的多线程程序。