Linux多线程编程(10分钟入门)
如今,几乎所有的电脑(操作系统)都支持同时执行多个任务,比如一边用迅雷下载资源,一边听歌,一边用 QQ 和好友聊天,这样的执行方式简称“并发”或者“并行”。
程序并行的常用实现方式有两种,分别叫做“多进程编程”和“多线程编程”。本节,我们教大家如何在 Linux 下进行多线程编程。并发和并行都指的是计算机可以同时执行多个任务,但严格来讲,它们是有区别的,只是本节不对它们做更细致的区分。
程序、进程和线程
学习多线程编程的实现方法之前,首先要搞清楚什么是线程,这就要从程序、进程和线程三者的关系和区别讲起。程序以源文件的方式存储在外存(比如硬盘、U盘等)中,只有运行的时候才会被载入内存。对于支持并行的操作系统来说,必须为每一个运行的程序分配所需的资源(内存空间、输入输出设备等),并确保同时运行的程序之间不会相互干扰,为此,操作系统将每一个运行着的程序视为一个进程:- 操作系统以进程为单位,为每个进程分配执行所需要的资源;
- 原则上,各个进程之间不允许访问对方的资源;
- 操作系统实时监控着每个进程的执行状态,必要时可以强制其终止执行。
- 代码:即应用程序的代码;
- 数据:包括全局变量、函数内的静态变量、堆空间的数据等;
- 进程空间:操作系统分配给进程的内存空间;
- 打开的文件:各个线程打开的文件资源,也可以为所有线程所共享,例如线程 A 打开的文件允许线程 B 进行读写操作。
多线程编程的实现方法
了解了程序、进程和线程之间的关系后,多线程的含义就很容易理解了,它指的是一个进程中拥有多个(≥2)线程。通常,我们将编写多线程程序的过程称为“多线程编程”。Linux 上编写多线程程序,可以借助本文的目标立足于教会大家编写入门级别的多线程程序,有关线程同步、线程死锁、线程属性等内容,建议您转至《多线程编程(C语言+Linux)》专题做系统的学习。t恤的英文
1) pthread_create()
pthread_create() 函数专门用来创建线程,语法格式如下:int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *),void *arg);
各个参数的含义是:- thread:接收一个 pthread_t 类型变量的地址,每个 pthread_t 类型的变量都可以表示一个线程。
- attr:手动指定新线程的属性,我们可以将其置为 NULL,表示新建线程遵循默认属性。
- start_routine:以函数指针的方式指明新建线程需要执行哪个函数。
- arg:向 start_routinue() 函数的形参传递数据。将 arg 置为 NULL,表示不传递任何数据。
- EAGAIN:系统资源不足,无法提供创建线程所需的资源。
- EINVAL:传递给 pthread_create() 函数的 attr 参数无效。
- EPERM:传递给 pthread_create() 函数的 attr 参数中,某些属性的设置为非法操作,程序没有相关的设置权限。
有关 pthread_create() 函数更详细的讲解,请阅读《创建线程》一文。t恤的英文
2) pthread_exit()
pthread_exit() 函数用于终止线程执行,语法格式如下:void pthread_exit(void *retval);
retval 参数指向的数据将作为线程执行结束时的返回值,如果不需要返回任何数据,将其置为 NULL 即可。注意,retval 不能指向函数内部的局部变量,否则会导致程序运行出错甚至崩溃。return 也可以终止线程执行,它和 pthread_exit() 之间有什么区别呢?我们已经在《终止线程(3种方法)》一文给出了答案。
3) pthread_cancel()
在多线程程序中,一个线程可以借助 pthread_cancel() 函数向另一个线程发送“终止执行”的信号。pthread_cancel() 函数的语法格式如下:int pthread_cancel(pthread_t thread);
thread 参数用于指定接收信号的目标线程。当成功发送“终止执行”的信号时,函数返回值为 0,否则返回非零数。再次强调,pthread_cancel() 函数只是向目标线程发送“终止执行”的信息,至于目标线程是否接收此信号,以及何时终止执行,由目标线程说了算,我们会在《终止线程执行,千万别踩这个坑!》一文做详细了解。t恤的英文
4) pthread_join()
pthread_join() 函数的功能主要有两个,分别是:- 接收目标线程执行结束时的返回值;
- 释放目标线程占用的进程资源。
int pthread_join(pthread_t thread, void ** retval);
thread 参数用于指定目标线程;retval 参数用于存储接收到的返回值。实际场景中,调用 pthread_join() 函数可能仅是为了及时释放目标线程占用的资源,并不想接收它的返回值,这种情况下可以将 retval 置为 NULL。pthread_join() 函数会一直阻塞当前线程,直至目标线程执行结束,阻塞状态才会消除。如果成功等到了目标线程执行结束(成功获取到目标线程的返回值),pthread_join() 函数返回数字 0,否则返回非零数。想全方位搞清楚 pthread_join() 函数的功能和用法,可阅读《获取线程函数的返回值》一文。t恤的英文
第一个多线程程序
接下来,我们利用上文学到的知识,编写第一个多线程程序:include程序中共有 3 个线程,分别是主线程,mythread1 线程和 mythread2 线程。mythread1 线程负责执行 Thread1() 函数,mythread2 线程负责执行 Thread2() 函数。主线程先后调用了两次 pthread_join() 函数,都会阻塞主线程,直至 mythread1 和 mythread2 线程执行完毕,阻塞状态才会消除。假设程序存储在 thread.c 文件中,调用 GCC 编译此程序:include //定义线程要执行的函数,arg 为接收线程传递过来的数据 void* Thread1(void* arg) { printf("http://www.weixueyuan.net\n"); return "Thread1成功执行"; } //定义线程要执行的函数,arg 为接收线程传递过来的数据 void* Thread2(void* arg) { printf("魏雪原\n"); return "Thread2成功执行"; } int main() { int res; //创建两个线程变量 pthread_t mythread1, mythread2; void* thread_result; //创建 mythread1 线程,执行 Thread1() 函数 res = pthread_create(&mythread1, NULL, Thread1, NULL); if (res != 0) { printf("线程创建失败"); return 0; } //创建 mythread2 线程,执行 Thread2() 函数 res = pthread_create(&mythread2, NULL, Thread2, NULL); if (res != 0) { printf("线程创建失败"); return 0; } //阻塞主线程,直至 mythread1 线程执行结束,用 thread_result 指向接收到的返回值,阻塞状态才消除。 res = pthread_join(mythread1, &thread_result); //输出线程执行完毕后返回的数据 printf("%s\n", (char*)thread_result); //阻塞主线程,直至 mythread2 线程执行结束,用 thread_result 指向接收到的返回值,阻塞状态才消除。 res = pthread_join(mythread2, &thread_result); printf("%s\n", (char*)thread_result); printf("主线程执行完毕"); return 0; }
[root@localhost ~] gcc thread.c -o thread.exe -lpthread
最终会生成一个名为 thread.exe 的可执行文件,执行如下命令即可看到执行结果:[root@localhost ~] ./thead.exehttp:www.weixueyuan.net魏雪原Thread1成功执行Thread2成功执行主线程执行完毕
总结
本节,我们了解了程序、进程和线程三者之间的关系,学会了如何编写一个简单的多线程程序。但是,与多线程编程相关的知识还有很多,比如实现线程同步,解决线程死锁问题、自定义线程的属性等,这些知识我们会在《多线程编程(C语言+Linux)》专题中给大家做详细的讲解。版权声明:
本站所有资源均为站长或网友整理自互联网或站长购买自互联网,站长无法分辨资源版权出自何处,所以不承担任何版权以及其他问题带来的法律责任,如有侵权或者其他问题请联系站长删除!站长QQ754403226 谢谢。