Linux中使用线程 1.第一个例子 Linux下创建线程的API接口是pthread_create(),它的完整定义是: intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*)void*arg); 当你的程序调用这个接口时,会产生一个线程,这个线程的入口函数是start_routine()。如果线程创建成功,这个接口会返回0。 start_routine()函数有一个参数,就是pthread_create的最后一个参数arg。这种设计可以帮助它在创建线程之前准备一些专有数据。最典型的用法是在C++编程时使用this指针。start_routine()有返回值,可以通过pthread_join()接口获取。 pthread_create()接口的第一个参数是一个返回参数。当一个新的线程调用成功后,会通过该参数将线程的句柄返回给调用者,以便对线程进行管理。 pthread_create()接口的第二个参数用于设置线程的属性。此参数是可选的。当你不需要修改线程的默认属性时,只要传NULL给它即可。具体线程有那些属性,我们后面会介绍。 将这段代码保存为thread.c文件,可以执行以下命令生成可执行文件: $gccthread.c-othread-lpthread 这段代码的执行结果可能如下所示: $./thread 这是主进程。 这是一个线程,arg=10. thread_ret=0. 注意,我说的是可能有这样的结果,在不同情况下可能会有所不同。因为这是一个多线程的程序,所以线程的代码可能先于24行的代码执行。 我们回头再分析一下这段代码。第18行调用pthread_create()接口创建新线程。这个线程的入口函数是start_thread(),给这个入口函数传递一个参数,参数值为10。这个新创建的线程要执行的任务很简单,但是会显示字符串“Thisisathreadandarg=10”,因为arg的参数值已经定义好了,就是10。线程接着修改将arg参数的值设置为0并将其作为线程的返回值返回给系统。同时,主进程做的就是继续判断线程是否创建成功。在我们的例子中,基本没有创建失败的可能。主进程会继续输出“Thisisthemainprocess”字符串,然后调用pthread_join()接口与刚才的创建合并。该接口的第一个参数是新创建线程的句柄,第二个参数将接受线程的返回值。pthread_join()接口会阻塞主进程的执行,直到合并线程的执行结束。由于线程结束后会返回0给系统,所以pthread_join()得到的线程返回值自然是0,这一点从输出“thread_ret=0”也得到了证实。 那么现在有个问题,就是pthread_join()接口是干什么的?什么是线程合并? 2。线程的合并与分离 首先我们要明确什么是线程合并。通过前面的叙述,读者已经了解到pthread_create()接口负责创建线程。那么线程也属于系统的资源,跟内存没什么区别,线程本身也占有一定的内存空间。一个众所周知的问题是,在C或C++编程中如果要通过malloc()或new分配一块内存,必须使用free()或delete来回收这块内存,否则就会出现famousmemory泄漏问题。由于线程和内存没有区别,创建了就必须有回收,否则就会出现另一个著名的资源泄漏问题,这也是一个严重的问题。那么线程的合并就是回收线程资源。 Linux下创建线程的API接口是pthread_create(),其完整定义为: intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*)void*arg); 当你的程序调用这个接口时,会产生一个线程,这个线程的入口函数是start_routine()。如果线程创建成功,该接口将返回0。 start_routine()函数有个参数,就是pthread_create的最后一个参数arg。这种设计可以帮助它在创建线程之前准备一些专有数据。最典型的用法是在C++编程时使用this指针。start_routine()有返回值,可以通过pthread_join()接口获取。 pthread_create()接口的第一个参数是一个返回参数。当一个新的线程调用成功后,会通过该参数将线程的句柄返回给调用者,以便对线程进行管理。 pthread_create()接口的第二个参数用于设置线程的属性。此参数是可选的。当你不需要修改线程的默认属性时,只要传NULL给它即可。具体线程有那些属性,我们后面会介绍。 好,那我们就用这些接口来完成Linux上的第一个多线程程序,如代码1所示: threadmerging是一种主动回收线程资源的方案。当进程或线程为其他线程调用pthread_join()接口时,线程将合并。该接口阻塞调用进程或线程,直到合并的线程终止。当合并线程结束时,pthread_join()接口会回收本线程的资源,并将本线程的返回值返回给合并线程。 线程合并对应的另一种线程资源回收机制是线程分离,调用接口为pthread_detach()。线程分离是将线程资源的回收工作交给系统自动完成,也就是说当被分离的线程结束时,系统会自动回收其资源。由于线程分离是启动系统的自动恢复机制,程序无法获取分离线程的返回值,这使得pthread_detach()接口只需要有一个参数,即分离线程句柄。 线程合并和线程分离都是为了回收线程资源,可以根据不同的业务场景酌情使用。不管是什么原因,都必须选择其中之一,否则就会造成资源泄漏问题,这和内存泄漏一样可怕。 创建线程时的默认可连接状态。如果不显示join阻塞调用或设置分离状态,即使线程结束返回或pthread_exit时,线程占用的栈和线程描述符也不会释放,造成资源泄漏。除了在创建线程时设置detachment参数外,还可以使用detach。实现方式有两种:父线程创建子线程后,执行pthread_detach(tid);或者,在子线程代码中执行pthread_detach(pthread_self())。 3。线程本地存储 线程可以共享内存地址空间,线程之间的数据交换可以非常快,这是线程最显着的优势。但是,多线程访问共享数据需要昂贵的同步开销,而且容易产生同步相关的bug。更麻烦的是有些数据根本不想共享,这又是一个缺点。可谓:“成也萧何,败也萧何”,就是这个道理。 C库中的errno就是最典型的例子。errno是一个全局变量,保存上次系统调用的错误代码。在单线程环境下是没有问题的。但是在多线程环境下,由于所有线程都可能修改errno,所以很难确定errno代表的是哪个系统调用错误码。这就是著名的“非线程安全(NonThread-Safe)”。 另外,从现代技术来看,很多情况下使用多线程的目的并不是为了并行处理共享数据(Linux下有更好的解决方案,后面会介绍)。更多的是由于多核CPU技术的引入,为了充分利用CPU资源,进行并行运算(互不干扰)。也就是说,大多数情况下每个线程只关心自己的数据,不需要和其他线程同步。 为了解决这些问题,有很多解决办法。例如使用不同名称的全局变量。但是对于errno这样名字已经固定的全局变量就没办法了。前面的内容提到过,线程栈中局部变量的分配是线程间不共享的。但是它有一个缺点,就是很难访问线程内部的其他函数。目前针对这个问题简单可行的解决方案是线程本地存储,即ThreadLocalStorage,简称TLS。使用TLS,errno反映了本线程最后一次系统调用的错误码,是线程安全的。分享一些视频资料给大家学习,也可以加裙子交流:1126743406linux基础http://www.makeru.com.cn/cour...linuxC语言内存管理http://www.makeru.com.cn/live...
