文章系列原因2017年初给自己定了一个小目标:学习Linux编程,通过网络分享自己的学习心得。为了完成这个小目标,我开始通过写文章来记录自己的学习心得。希望年底能完成24个Linux相关的学习文档,实现我的小目标。这是本系列的第一篇文章,是我最近对Linux多线程学习的一个总结。什么是线程我们来看看维基百科是如何定义线程的:线程(英文:thread)是操作系统能够进行操作调度的最小单位。它包含在流程中,是流程中的实际操作单元。线程是指进程中的单个控制流序列。一个进程中可以有多个线程并行运行,每个线程并行执行不同的任务。在UnixSystemV和SunOS中,也称为轻量级进程(lightweightprocesses),但轻量级进程是指内核线程(kernelthreads),而用户线程(userthreads)则称为线程。一个进程可以包含多个线程,每个线程都有自己的调用堆栈、自己的寄存器上下文和自己的线程本地存储。每个线程同时执行不同的任务。因为我没有写过进程相关的文章,这里就不进一步解释进程和线程的区别了。我将在流程或单独的文章中解释它们的异同。线程生命周期的四种状态线程在其生命周期中有四种状态,分别是就绪、运行、阻塞和终止。下表解释了这四种状态:状态意味着就绪线程可以运行,但正在等待可用处理器正在运行,线程正在运行。在多核系统中,可能有多个线程同时运行。被阻塞的线程正在等待处理器以外的其他条件。要在特定条件下转换其状态,下图显示了线程如何转换其状态:要创建线程,我们使用pthread_create()函数。函数说明如下:intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);args:pthread_t*thread:指向新线程的线程号的指针,如果创建成功,将线程号写回thread指向的内存空间constpthread_attr_t*attr:指向新线程属性的指针void*(*start_routine)(void*):要执行的函数的地址bythenewthread(callbackfunction)void*arg:指向新线程要执行的函数的参数的指针return:线程创建的状态,0表示成功,非零失败每个成功创建的线程都有一个线程号,我们可以通过pthread_self()函数获取调用该函数的线程的线程号。需要注意的一点是,如果需要编译pthread_XX()相关的函数,需要在编译的时候加上-pthread。结束基本控制线程结束一个线程,我们通常有以下方法:方法说明exit()终止整个进程,并释放整个进程的内存空间pthread_exit()终止线程,但不释放非分离线程的内存空间pthread_cancel()其他线程通过信号exit()取消当前线程会终止整个进程,所以我们几乎不用它来结束线程。我们经常使用pthread_cancel()和pthread_exit()函数来结束线程。这里,我们重点关注函数pthread_exit():voidpthread_exit(void*retval);args:void*retval:指向线程结束代码的指针return:None要结束一个线程,只要在线程调用的函数中加上pthread_exit(X)就可以了,但是有一点需要特别注意:如果一个线程是非-detached(默认创建的线程是非detached的)并且线程没有使用pthread_join(),线程结束后不会释放线程。内存空间。这导致该线程成为“僵尸线程”。“僵尸线程”会占用大量的系统资源,所以我们要避免“僵尸线程”的出现。线程连接上一节我们提到了pthread_join()函数。在本节中,让我们看一下这个函数:intpthread_join(pthread_tthread,void**retval);args:pthread_tthread:连接线程号的线程void**retval:指向连接线程返回码的指针return:线程连接状态,0表示成功,非0表示失败当调用pthread_join(),当前线程会被阻塞,直到连接上,直到调用线程结束,当前线程才会恢复执行。当pthread_join()函数返回时,被调用线程真正结束,其内存空间将被释放(如果被调用线程是非分离的)。这里需要注意三点:释放的内存空间只是系统空间,必须手动清除程序分配的空间,比如malloc()分配的空间。一个线程只能被一个线程连接。被连接的线程必须是非分离的,否则会连接失败。线程分离在上一节中,我们在讲pthread_join()的时候说过,只有非分离线程才能使用pthread_join()。这一节,我们就来看看什么是线程分离。在Linux中,线程要么是可连接的,要么是可分离的。当我们创建线程时,线程默认是可连接的。可连接线程和可分离线程之间有以下区别:线程类型表示可连接线程可以被其他线程回收或杀死。在它被杀死之前,内存空间不会自动释放。一个可分离的线程不能被其他线程回收或杀死,它的内存空间在它终止时被系统自动释放。我们可以看到,对于joinable线程,它并不会自动释放自己的内存空间。所以对于这种类型的线程,我们必须配合使用pthread_join()函数。对于可分离函数,我们不能使用pthread_join()函数。要分离一个线程,我们有两种方法:1)通过修改线程属性使其成为一个可分离的线程;2)通过调用函数pthread_detach()使新线程成为可分离线程。下面是pthread_detach()函数的说明:intpthread_detach(pthread_tthread);args:pthread_tthread:需要分离的线程的线程号return:线程分离的状态,0表示成功,非0表示失败我们只需要提供需要分离的线程的线程号,你可以使它从一个可连接的线程变成一个可分离的线程。主线程在上面几节中,我们讲了线程的创建、终止和连接。在本节中,我们将了解一个特殊的线程——主线程。在C程序中,main(intargc,char**argv)是一个主线程。我们可以在主线程中做任何普通线程可以做的事情,但是它和普通线程有很大的不同:当主线程返回或者结束时,会导致进程结束,进程结束会导致所有线程结束。为了不让主线程结束所有线程,根据我们之前了解到的,有几种解决方法:不让主线程返回或结束(在return前加while(1)语句)。调用pthread_exit()结束主线程。当主线程返回时,它的内存空间就会被释放。return前调用pthread_join(),此时主线程会被阻塞,直到被连接的线程执行完毕才会继续运行。这三种方法中,前两种很少用,第三种是常用的方法。小结本文主要介绍了线程的基本概念,线程的生命周期和状态,线程的创建、结束、连接、分离和主线程。在下一篇文章中,我将介绍多线程中的关键点——同步。如果您觉得本文对您有帮助,请点赞支持,谢谢!
