linux多线程程序设计
- 格式:ppt
- 大小:154.00 KB
- 文档页数:20
Unix_Linux_Windows_OpenMP多线程编程第三章 Unix/Linux 多线程编程[引言]本章在前面章节多线程编程基础知识的基础上,着重介绍 Unix/Linux 系统下的多线程编程接口及编程技术。
3.1 POSIX 的一些基本知识POSIX 是可移植操作系统接口(Portable Operating SystemInterface)的首字母缩写。
POSIX 是基于 UNIX 的,这一标准意在期望获得源代码级的软件可移植性。
换句话说,为一个 POSIX 兼容的操作系统编写的程序,应该可以在任何其它的 POSIX 操作系统(即使是来自另一个厂商)上编译执行。
POSIX 标准定义了操作系统应该为应用程序提供的接口:系统调用集。
POSIX是由 IEEE(Institute of Electrical andElectronic Engineering)开发的,并由 ANSI(American National Standards Institute)和 ISO(International StandardsOrganization)标准化。
大多数的操作系统(包括 Windows NT)都倾向于开发它们的变体版本与 POSIX 兼容。
POSIX 现在已经发展成为一个非常庞大的标准族,某些部分正处在开发过程中。
表 1-1 给出了 POSIX 标准的几个重要组成部分。
POSIX 与 IEEE 1003 和 2003 家族的标准是可互换的。
除 1003.1 之外,1003 和 2003 家族也包括在表中。
管理 POSIX 开放式系统环境(OSE) 。
IEEE 在 1995 年通过了这项标准。
ISO 的1003.0版本是 ISO/IEC 14252:1996。
被广泛接受、用于源代码级别的可移植性标准。
1003.1 提供一个操作系统的C 语1003.1 言应用编程接口(API) 。
IEEE 和 ISO 已经在 1990 年通过了这个标准,IEEE 在1995 年重新修订了该标准。
linux多线程pthread常用函数详解Linux多线程是指在Linux操作系统中运行的多个线程。
线程是执行程序的基本单位,它独立于其他线程而存在,但共享相同的地址空间。
在Linux中,我们可以使用pthread库来实现多线程程序。
本文将详细介绍pthread库中常用的函数,包括线程的创建、退出、同步等。
一、线程创建函数1. pthread_create函数pthread_create函数用于创建一个新线程。
其原型如下:cint pthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);参数说明:- thread:用于存储新线程的ID- attr:线程的属性,通常为NULL- start_routine:线程要执行的函数地址- arg:传递给线程函数的参数2. pthread_join函数pthread_join函数用于等待一个线程的结束。
其原型如下:int pthread_join(pthread_t thread, void retval);参数说明:- thread:要等待结束的线程ID- retval:用于存储线程的返回值3. pthread_detach函数pthread_detach函数用于将一个线程设置为分离状态,使其在退出时可以自动释放资源。
其原型如下:cint pthread_detach(pthread_t thread);参数说明:- thread:要设置为分离状态的线程ID二、线程退出函数1. pthread_exit函数pthread_exit函数用于退出当前线程,并返回一个值。
其原型如下:cvoid pthread_exit(void *retval);参数说明:- retval:线程的返回值2. pthread_cancel函数pthread_cancel函数用于取消一个线程的执行。
linux多线程编程的书-回复
以下是一些关于Linux多线程编程的书籍推荐:
1.《Unix/Linux多线程程序设计》:这本书详细介绍了Unix/Linux环境下的多线程编程技术,包括POSIX threads API、线程同步机制、线程间的通信等内容。
2.《Linux多线程服务端编程》:这本书主要针对Linux环境下的高性能、高并发服务器编程,深入讲解了多线程、异步I/O、锁机制、线程池等关键技术。
3.《Linux多线程编程》:这本书从理论和实践两个角度出发,全面介绍了Linux多线程编程的相关知识,包括线程的创建和管理、线程间的同步和通信、线程调度策略等内容。
4.《Linux/Posix多线程程序设计》:这本书详细介绍了Linux/Posix环境下的多线程编程技术,包括线程的创建和终止、线程间的同步和通信、线程的调度和优先级等内容。
5.《Java多线程编程实战》:虽然这本书主要是关于Java多线程编程的,但是其中很多概念和技巧也适用于Linux环境下的多线程编程,特别是对于那些使用Java进行Linux服务器开发的开发者来说,非常有参考价值。
以上这些书籍都是比较经典的多线程编程参考书籍,可以根据自己的需求和背景选择适合自己的书籍进行学习。
多线程编程-同步在上一章节中,我们通过程序示例,见证了单线程世界中不可能发生的事件(一个数既是奇数又是偶数)在多线程环境中是怎样分分钟发生的,我通过细分程序执行步骤,分析了奇异事件发生的过程,并探明了其原因:一个线程在对全局变量gcnt进行两次判读的过程中,另一个线刚好改变了这个变量的值。
在多线程编程术语中,称这两个线程同时进入了临界区域。
所谓临界区域,是指多线程环境下两个及以上线程同时执行可能会导致冲突的一段代码。
在上一章节的示例中,这几行代码就是一个临界区域:gcnt++;if (gcnt % 2){if (!(gcnt % 2)) printf("[%d] : %d\n", id, gcnt);}冲突之所以会发生,是因为临界区域的代码,通常需要很多个CPU指令周期才能完成,其运行过程随时可能被打断(进行了线程调试),CPU去运行另外的线程,如果这个线程刚好也进入了临界区域,则异常的程序状态极可能会发生。
如果当某个线程进入临界区域,在其退出区域之前,其他的线程无论如何也不能进入该区域,那么冲突就不会发生。
Linux提供了这种保证多线程进入临界区域互斥的机制,这正是本章节所要介绍的内容:线程锁。
我们今天的示例程序还是在上一章节的示例上改进而来的,我们的任务就是使用线程锁,保证“一个数既是奇数又是偶数”的奇异事件在多线程环境下也不发生,代码如下:#include <pthread.h>#include <stdio.h>#include <unistd.h>int gcnt = 0;pthread_mutex_t g_mutex;void *thread_task(void *arg){int id = (int)arg;while (1){pthread_mutex_lock(&g_mutex);gcnt++;if (gcnt % 2)if (!(gcnt % 2)) printf("[%d] : %d\n", id, gcnt);}pthread_mutex_unlock(&g_mutex);usleep(1);}return NULL;}int main(int argc, char *argv[]){pthread_t thr;pthread_mutex_init(&g_mutex, NULL);pthread_create(&thr, NULL, thread_task, (void *)1);pthread_create(&thr, NULL, thread_task, (void *)2);thread_task((void *)0);return 0;}今天的程序相对于上章的代码,改动非常小,只添加了四行,已使用红色加粗标注。
linux下的CC++多进程多线程编程实例详解linux下的C\C++多进程多线程编程实例详解1、多进程编程#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int main(){pid_t child_pid;/* 创建⼀个⼦进程 */child_pid = fork();if(child_pid == 0){printf("child pid\n");exit(0);}else{printf("father pid\n");sleep(60);}return 0;}2、多线程编程#include <stdio.h>#include <pthread.h>struct char_print_params{char character;int count;};void *char_print(void *parameters){struct char_print_params *p = (struct char_print_params *)parameters;int i;for(i = 0; i < p->count; i++){fputc(p->character,stderr);}return NULL;}int main(){pthread_t thread1_id;pthread_t thread2_id;struct char_print_params thread1_args;struct char_print_params thread2_args;thread1_args.character = 'x';thread1_args.count = 3000;pthread_create(&thread1_id, NULL, &char_print, &thread1_args);thread2_args.character = 'o';thread2_args.count = 2000;pthread_create(&thread2_id, NULL, &char_print, &thread2_args);pthread_join(thread1_id, NULL);pthread_join(thread2_id, NULL);return 0;}3、线程同步与互斥1)、互斥pthread_mutex_t mutex;pthread_mutex_init(&mutex, NULL);/*也可以⽤下⾯的⽅式初始化*/pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mutex);/* 互斥 */thread_flag = value;pthread_mutex_unlock(&mutex);2)、条件变量int thread_flag = 0;pthread_mutex_t mutex;pthread_cond_t thread_flag_cv;\void init_flag(){pthread_mutex_init(&mutex, NULL);pthread_cond_init(&thread_flag_cv, NULL);thread_flag = 0;}void *thread_function(void *thread_flag){while(1){pthread_mutex_lock(&mutex);while(thread_flag != 0 ){pthread_cond_wait(&thread_flag_cv, &mutex);}pthread_mutex_unlock(&mutex);do_work();}return NULL;}void set_thread_flag(int flag_value){pthread_mutex_lock(&mutex);thread_flag = flag_value;pthread_cond_signal(&thread_flag_cv);pthread_mutex_unlock(&mutex);}感谢阅读,希望能帮助到⼤家,谢谢⼤家对本站的⽀持!。
Linux 多线程编程问题1重入问题传统的UNIX没有太多考虑线程问题,库函数里过多使用了全局和静态数据,导致严重的线程重入问题。
1.1–D_REENTRANT /-pthread和errno的重入问题。
所先UNIX的系统调用被设计为出错返回-1,把错误码放在errno中(更简单而直接的方法应该是程序直接返回错误码,或者通过几个参数指针来返回)。
由于线程共享所有的数据区,而errno是一个全局的变量,这里产生了最糟糕的线程重入问题。
比如:do {bytes = recv(netfd, recvbuf, buflen, 0);} while (bytes != -1 && errno != EINTR);在上面的处理recv被信号打断的程序里。
如果这时连接被关闭,此时errno应该不等于EINTR,如果别的线程正好设置errno为EINTR,这时程序就可能进入死循环。
其它的错误码处理也可能进入不可预测的分支。
在线程需求刚开始时,很多方面技术和标准(TLS)还不够成熟,所以在为了解决这个重入问题引入了一个解决方案,把errno定义为一个宏:extern int *__errno_location (void);#define errno (*__errno_location())在上面的方案里,访问errno之前先调用__errno_location()函数,线程库提供这个函数,不同线程返回各自errno的地址,从而解决这个重入问题。
在编译时加-D_REENTRANT就是启用上面的宏,避免errno重入。
另外-D_REENTRANT还影响一些stdio的函数。
在较高版本的gcc里,有很多嵌入函数的优化,比如把printf(“Hello\n”);优化为puts(“hello\n”);之类的,有些优化在多线程下有问题。
所以gcc引入了–pthread 参数,这个参数出了-D_REENTRANT外,还校正一些针对多线程的优化。
linux和windows通用的多线程方法
多线程是一种在计算机程序中处理多个相似或相关的任务的技术。
无论是在Linux还是Windows中,多线程的实现都是类似的。
以下是一些通用的多线程方法:
1. 创建线程:使用线程库中提供的函数,例如在Linux中使用pthread_create(),在Windows中使用CreateThread()。
2. 同步线程:使用同步机制来保护共享资源,例如在Linux中使用pthread_mutex_lock()和pthread_mutex_unlock(),在Windows 中使用CriticalSection。
3. 线程间通信:使用消息传递或共享内存等机制来实现线程间通信。
在Linux中,可以使用管道、共享内存和信号量等。
在Windows 中,可以使用命名管道和邮槽等。
4. 线程池:创建一个线程池来管理多个线程,这样可以避免频繁地创建和销毁线程,提高效率。
5. 轮询:使用循环不断地检查线程是否完成任务,从而避免阻塞主线程。
总的来说,多线程在Linux和Windows中的实现都是类似的,只要掌握了基本的多线程概念和方法,就可以在两个操作系统中进行开发。
Linux下c语⾔多线程编程引⾔ 线程(thread)技术早在60年代就被提出,但真正应⽤多线程到中去,是在80年代中期,solaris是这⽅⾯的佼佼者。
传统的Unix也⽀持线程的概念,但是在⼀个进程(process)中只允许有⼀个线程,这样多线程就意味着多进程。
现在,多 为什么有了进程的概念后,还要再引⼊线程呢?使⽤多线程到底有哪些好处?什么的系统应该选⽤多线程?我们⾸先必须回答这些问题。
使⽤多线程的理由之⼀是和进程相⽐,它是⼀种⾮常"节俭"的多任务操作⽅式。
我们知道,在Linux系统下,启动⼀个新的进程必须分配给它独⽴的地址空间,建⽴众多的数据表来维护它的代码段、堆栈段和数据段,这是⼀种"昂贵"的多任务⼯作⽅式。
⽽运⾏于⼀个进程中的多个线程,它们彼此之间使⽤相同的地址空间,共享⼤部分数据,启动⼀个线程所花费的空间远远⼩于启动⼀个进程所花费的空间,⽽且,线程间彼此切换所需的时间也远远⼩于进程间切换所需要的时间。
使⽤多线程的理由之⼆是线程间⽅便的机制。
对不同进程来说,它们具有独⽴的数据空间,要进⾏数据的传递只能通过通信的⽅式进⾏,这种⽅式不仅费时,⽽且很不⽅便。
线程则不然,由于同⼀进程下的线程之间共享数据空间,所以⼀个线程的数据可以直接为其它线程所⽤,这不仅快捷,⽽且⽅便。
当然,数据的共享也带来其他⼀些问题,有的变量不能同时被两个线程所修改,有的⼦程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地⽅。
除了以上所说的优点外,不和进程⽐较,多线程程序作为⼀种多任务、并发的⼯作⽅式,当然有以下的优点: 1) 提⾼应⽤程序响应。
这对图形界⾯的程序尤其有意义,当⼀个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应、、菜单的操作,⽽使⽤多线程技术,将耗时长的操作(time consuming)置于⼀个新的线程,可以避免这种尴尬的情况。
linux多线程的实现方式Linux是一种支持多线程的操作系统,它提供了许多不同的方式来实现多线程。
本文将介绍Linux多线程的几种实现方式。
1. 线程库Linux提供了线程库,包括POSIX线程库(Pthreads)和LinuxThreads。
Pthreads是一种由IEEE组织制定的标准线程库,它提供了一组线程API,可以在不同的操作系统上实现。
LinuxThreads 是Linux内核提供的线程实现,不同于Pthreads,它不是标准线程库,但具有更好的性能。
使用线程库可以方便地创建和管理线程,线程库提供了许多API 函数,例如pthread_create(),pthread_join(),pthread_mutex_lock()等,可以在程序中使用这些API函数来实现多线程。
2. 多进程在Linux中,多进程也是一种实现多线程的方式。
每个进程都可以有自己的线程,进程之间也可以通过IPC机制进行通信。
多进程的优点是可以更好地利用多核CPU,因为每个进程都可以在不同的CPU核心上运行。
但是,多进程的开销比多线程大,因为每个进程都需要拥有自己的地址空间和运行环境。
3. 线程池线程池是一种常见的多线程实现方式。
线程池中有多个线程可以处理任务,任务可以通过任务队列来进行分发。
当任务到达时,线程池中的线程会从任务队列中取出任务并处理。
线程池的优点是可以重复利用线程,减少创建和销毁线程的开销。
线程池还可以控制线程的数量,避免过多线程导致的性能下降。
4. 协程协程是一种轻量级线程,它不需要操作系统的支持,可以在用户空间中实现。
协程基于线程,但是不需要线程上下文切换的开销,因为协程可以在同一个线程内进行切换。
协程的优点是可以更好地利用CPU,因为不需要线程上下文切换的开销。
协程还可以更好地控制并发性,因为协程的切换是由程序员控制的。
总结Linux提供了多种实现多线程的方式,每种方式都有其优点和缺点。
在选择多线程实现方式时,需要考虑到应用程序的特点和需求,选择最适合的实现方式。
一种Linux多线程应用下内存池的设计与实现一种Linux多线程应用下内存池的设计与实现摘要:对内存池中内存块获取、分配机制、内存块大小、内存释放,以及在多线程环境下的安全处理等细节进行了研究,保证了在多线程环境下能够快速同时采用一种基于数组的链表机制,改进内存池中内存块的查找算法,将其时间复杂度稳定在O(1),避免了传统内存池中请求的线程数目过多时,引发的获取内存块性能下降的问题。
同时在内部设置管理线程,动态增加或删除空闲的内存块。
实验结果表明,改进后的内存池与传统的内存分配方式相比消耗更小,效率更好。
关键词:内存池;内存块查找算法;Linux;多线程动态内存管理非常耗时,对效率影响很大,然而在实际的编程应用中,却不可避免地经常要用到堆中的内存。
但是通过Malloc函数或New等进行的内存分配存在先天缺陷:(1)利用默认的内存管理函数在堆上分配和释放内存需要花费很多时间;(2)随着时间的推移,堆上会形成许多内存碎片,在应用程序进行内存申请操作将受到更大的影响,导致应用程序的运行越来越慢[1-3]。
当应用程序需要对固定大小的对象经常性地申请内存时,常会采用内存池(Memory Pool)技术来提高内存管理效率。
经典的内存池做法是一次性分配大量大小相同的小块内存,通过该技术可以极大地加快内存分配/释放过程。
内存池技术通过批量申请内存,降低了内存申请次数,从而使操作节省了时间。
在减少了内存碎片产生的同时,对性能的提升有显著的帮助。
综上,内存池有其巨大的优势,但是原有的内存池也存在一定的缺陷。
在多线程场合下应用时,每个新产生的线程如何在O(1)时间内获取内存块,如何保证其安全有效性,以及如何管理内存块的数量方面存在一定的不足的,本文对此进行研究,并给出一种新的解决方案。
1内存池制作原理以及工作流程本内存池基于多线程环境,需要考虑到多线程下数据的安全,以及快速获取内存块等条件。
在获取内存块索引号时,采用加锁的方式,虽然会耗费一定的时间,但是运行安全得到了保障。
linux 多线程程序的返回值多线程程序是在现代操作系统中常见的并发编程模型之一。
多线程可以在单个程序中同时运行多个线程,每个线程具有其独立的执行流程和执行上下文。
在Linux系统中,多线程程序的返回值可以通过多种方式实现,下面将详细介绍。
1.线程函数返回值:在Linux中,多线程程序中的每个线程都可以通过其线程函数的返回值来返回一个值。
线程函数是通过pthread_create函数创建的线程的入口点,它接受一个指向线程函数的指针作为参数。
例如:```cppvoid* thread_func(void* arg) {//线程函数的实现// ...return (void*)result; //返回线程的返回值}int main() {pthread_t thread;pthread_create(&thread, NULL, thread_func, NULL);void* result;pthread_join(thread, &result); //阻塞主线程,等待子线程结束,并获取返回值int return_val = (int)result; //转换返回值//处理返回值// ...return 0;}```在上面的例子中,线程函数thread_func通过return语句返回一个指针类型(void*),然后在主线程中通过pthread_join函数来等待子线程结束,并获取其返回值。
2.全局变量:另一种实现多线程程序返回值的方式是使用全局变量。
多个线程可以共享相同的全局变量,并且可以通过该变量传递返回值。
例如:```cppint global_result = 0;void* thread_func(void* arg) {//线程函数的实现// ...global_result = result; //设置全局变量的值pthread_exit(NULL); //退出线程}int main() {pthread_t thread;pthread_create(&thread, NULL, thread_func, NULL);pthread_join(thread, NULL); //等待子线程结束//处理返回值int return_val = global_result;// ...return 0;}```在上面的例子中,线程函数thread_func将结果存储在全局变量global_result中,然后在主线程中可以直接访问该变量来获取返回值。
linux多线程编程实验心得在进行Linux多线程编程实验后,我得出了一些心得体会。
首先,多线程编程是一种高效利用计算机资源的方式,能够提高程序的并发性和响应性。
然而,它也带来了一些挑战和注意事项。
首先,线程同步是多线程编程中需要特别关注的问题。
由于多个线程同时访问共享资源,可能会引发竞态条件和数据不一致的问题。
为了避免这些问题,我学会了使用互斥锁、条件变量和信号量等同步机制来保护共享数据的访问。
其次,线程间通信也是一个重要的方面。
在实验中,我学会了使用线程间的消息队列、管道和共享内存等方式来实现线程间的数据传递和协作。
这些机制可以帮助不同线程之间进行有效的信息交换和协调工作。
此外,线程的创建和销毁也需要注意。
在实验中,我学会了使用pthread库提供的函数来创建和管理线程。
同时,我也了解到线程的创建和销毁是需要谨慎处理的,过多或过少的线程都可能导致系统资源的浪费或者性能下降。
在编写多线程程序时,我还学会了合理地划分任务和资源,以充分发挥多线程的优势。
通过将大任务拆分成多个小任务,并将其分配给不同的线程来并行执行,可以提高程序的效率和响应速度。
此外,我还学会了使用调试工具来分析和解决多线程程序中的问题。
通过使用gdb等调试器,我可以观察线程的执行情况,查找潜在的错误和死锁情况,并进行相应的修复和优化。
总结起来,通过实验我深刻认识到了多线程编程的重要性和挑战性。
合理地设计和管理线程,正确处理线程同步和通信,以及使用调试工具进行分析和修复问题,都是编写高效稳定的多线程程序的关键。
通过不断实践和学习,我相信我能够更好地应用多线程编程技术,提升程序的性能和可靠性。
利用多核多线程进行程序优化简介:大家也许还记得2005 年 3 月C++ 大师Herb Sutter 在Dr.Dobb’s Journal 上发表了一篇名为《免费的午餐已经结束》的文章。
文章指出:现在的程序员对效率、伸缩性、吞吐量等一系列性能指标相当忽视,很多性能问题都仰仗越来越快的CPU 来解决。
但CPU 的速度在不久的将来,即将偏离摩尔定律的轨迹,并达到一定的极限。
所以,越来越多的应用程序将不得不直面性能问题,而解决这些问题的办法就是采用并发编程技术。
样例程序程序功能:求从1一直到APPLE_MAX_VALUE (100000000)相加累计的和,并赋值给apple 的a和b;求orange 数据结构中的a[i]+b[i ] 的和,循环ORANGE_MAX_VALUE(1000000)次。
说明:1. 由于样例程序是从实际应用中抽象出来的模型,所以本文不会进行test.a=test.b=test.b+sum、中间变量(查找表)等类似的优化。
2. 以下所有程序片断均为部分代码,完整代码请参看本文最下面的附件。
清单1. 样例程序回页首K-Best 测量方法在检测程序运行时间这个复杂问题上,将采用Randal E.Bryant和David R. O’Hallaron提出的K 次最优测量方法。
假设重复的执行一个程序,并纪录K 次最快的时间,如果发现测量的误差ε 很小,那么用测量的最快值表示过程的真正执行时间,称这种方法为“ K 次最优(K-Best)方法”,要求设置三个参数:K: 要求在某个接近最快值范围内的测量值数量。
ε 测量值必须多大程度的接近,即测量值按照升序标号V1, V2, V3, … , Vi, … ,同时必须满足(1+ ε)Vi >= VkM: 在结束测试之前,测量值的最大数量。
按照升序的方式维护一个K 个最快时间的数组,对于每一个新的测量值,如果比当前K 处的值更快,则用最新的值替换数组中的元素K ,然后再进行升序排序,持续不断的进行该过程,并满足误差标准,此时就称测量值已经收敛。
Linuxthreads 是一个用于创建和管理多线程的库,它提供了简单的接口以便程序员在Linux 系统上编写多线程程序。
要编译一个使用Linuxthreads 的程序,你需要遵循以下步骤:1. 安装Linuxthreads 库在大多数Linux 发行版中,Linuxthreads 库已经预先安装。
你可以使用以下命令检查是否已安装:```bashpkg-config --modversion libthreads```如果尚未安装,请根据你的Linux 发行版安装手册安装相应的库。
2. 编写多线程程序使用Linuxthreads 编写一个简单的多线程程序,如下所示:```c#include <stdio.h>#include <stdlib.h>#include <thread.h>void thread_function(void *arg) {printf("Hello from thread %lu\n", (unsigned long)arg);}int main() {pthread_t threads[10];int rc;rc = pthread_create(&threads[0], NULL, thread_function, (void *)1);if (rc) {printf("Error creating thread: %s\n", strerror(rc));exit(EXIT_FAILURE);}rc = pthread_create(&threads[1], NULL, thread_function, (void *)2);if (rc) {printf("Error creating thread: %s\n", strerror(rc));exit(EXIT_FAILURE);}for (int i = 2; i < 10; i++) {rc = pthread_create(&threads[i], NULL, thread_function, (void *)(i + 1));if (rc) {printf("Error creating thread: %s\n", strerror(rc));exit(EXIT_FAILURE);}}for (int i = 0; i < 10; i++) {rc = pthread_join(threads[i], NULL);if (rc) {printf("Error joining thread: %s\n", strerror(rc));exit(EXIT_FAILURE);}}printf("All threads joined.\n");return 0;}```这个程序创建了10 个线程,并让它们执行`thread_function` 函数。