申明:本学习笔记是在该教程的基础上结合自己的学习情况进行的总结,不是原创,想要看原版的请看C语言中文网的多线程编程(C语言+Linux),该网站有很多好的编程学习教程,尤其是关于C语言的。
大多数操作系统都支持同时执行多个程序,包括常见的 Windows、Linux、Mac OS X 操作系统等。为了避免多个程序访问系统资源(包括文件资源、I/O 设备、网络等)时产生冲突,操作系统会将可能产生冲突的系统资源保护起来,阻止应用程序直接访问。如果程序中需要访问被操作系统保护起来的资源,需使用操作系统规定的方法(函数、命令),我们习惯将这些调用方法(函数、命令)称为接口(Application Programming Interface,简称 API)。
事实上,无论我们用哪种编程语言编写多线程程序,最终都要借助操作系统预留的接口实现。接下来,我们将为您讲解如何借助 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 库中。
第一个多线程程序
分析如下程序:程序名为firstThread.c
#include <stdio.h>
#include <pthread.h>
//定义线程要执行的函数,arg 为接收线程传递过来的数据
void *Thread1(void *arg)
{
printf("http://c.biancheng.net\n");
return "Thread1成功执行";
}
//定义线程要执行的函数,arg 为接收线程传递过来的数据
void* Thread2(void* arg)
{
printf("C语言中文网\n");
return "Thread2成功执行";
}
int main()
{
int res;
pthread_t mythread1, mythread2;
void* thread_result;
/*创建线程
&mythread:要创建的线程
NULL:不修改新建线程的任何属性
ThreadFun:新建线程要执行的任务
NULL:不传递给 ThreadFun() 函数任何参数
返回值 res 为 0 表示线程创建成功,反之则创建失败。
*/
res = pthread_create(&mythread1, NULL, Thread1, NULL);
if (res != 0) {
printf("线程创建失败");
return 0;
}
res = pthread_create(&mythread2, NULL, Thread2, NULL);
if (res != 0) {
printf("线程创建失败");
return 0;
}
/*
等待指定线程执行完毕
mtThread:指定等待的线程
&thead_result:接收 ThreadFun() 函数的返回值,或者接收 pthread_exit() 函数指定的值
返回值 res 为 0 表示函数执行成功,反之则执行失败。
*/
res = pthread_join(mythread1, &thread_result);
//输出线程执行完毕后返回的数据
printf("%s\n", (char*)thread_result);
res = pthread_join(mythread2, &thread_result);
printf("%s\n", (char*)thread_result);
printf("主线程执行完毕");
return 0;
}
下面我们演示分别在linux和Windows下编译执行这个多线程程序。
Linux下编译执行
先看一下在linux环境下头文件(pthread.h)和库文件(libpthread.a)分别存放在哪个目录下:
编译命令为:
gcc firstThread.c -o out -lpthread
或者gcc firstThread.c -o out -pthread
编译成功之后,我们执行生成的可执行文件
./out
在Windows下编译执行
可以看出多线程执行环境依赖的是头文件(pthread.h)和库文件(libpthread.a),因此只要在Windows的编译环境下存在这个头文件和库文件,将其加入包含路径就可以了。
在DEVC++安装目录下,正好存在这两个文件,这两个文件存在的目录是D:\Dev-Cpp\MinGW64\x86_64-w64-mingw32\include和D:\Dev-Cpp\MinGW64\x86_64-w64-mingw32\lib,而这两个路径是被默认加入了DevC++的包含路径的,因此我们可以直接在DEVC++上编译执行上面的多线程程序。
用上面那段代码放到DEVC++上直接编译会报错,如下图:
提示说我们进行了一个非法的类型转换,从const void转换成void**,这个其实知识函数参数的一个类型转换,不影响程序的执行,通过下面的提示,我们只要在编译命令下加入 -fpermissive 即可。
现在就可以正常编译运行了
可以看到下面的提示依旧会有警告,但是已经可以正常编译运行了。
总结
我们上面分别在linux和Windows下编译运行了我们的第一个多线程程序,可以看到多线程的编译环境依赖于头文件(pthread.h)和库文件(libpthread.a)。