fork()创建进程和使用POSIX Pthreads库创建线程有一些差别
使用fork()创建进程的特点:
1、代价昂贵,通常子进程需要复制进程的整个上下文,比如数据等。
2、进程间的通信方式比较复杂,比如使用管道、消息、共享内存等方法。
3、操作系统在实现进程间的切换比线程间切换更费时。
使用POSIX Pthreads库创建线程的特点:
1、线程可使用存在于进程种的资源,因此创建进程比创建线程更快。
2、线程间的通信方式更容易,比如通过进程中的变量,可以让多个线程共享数据。
3、操作系统对线程的切换比对进程的切换更容易和快速。
POSIX Pthreads线程库中提供的创建线程的函数是pthread_create(),函数的原型是:
int pthread_create(pthread_t * thread,
pthread_attr_t * attr,
void * (*start_routine)(void *),
void * arg);
第一个参数是pthread_t类型的指针,这个指针指向用来存放当前线程创建成功后所创建的线程标志,一般情况下需要在创建线程前在堆中或者在栈中分配内存空间。
第二个参数表明被创建的线程可以拥有的属性,这个参数可以在创建线程前指向一个pthread_attr_t的数据对象,或者为NULL。当attr为NULL的时侯,表示创建的线程拥有默认的属性:线程是可加入的,其拥有普通的调度策略且不是实时的。
第三个参数是一个函数指针,此函数指向线程的实现函数。可以看到,线程的实现函数的返回类型是 void *,而且其传递的参数类型是 void *。
第四个参数arg是void *类型的,此参数指向实际线程处理函数执行的时侯所需要得参数,由于传递的参数是void *类型,所以在实际编写线程处理函数的时侯,需要根据实际情况,在线程处理函数中进行转化,用来得到需要的类型数据。
pthread_create()
函数调用成功后,返回值为零,如果发生错误,返回非负错误代码。如果返回错误代码为EAGAIN,表示系统没有组构的资源创建新的线程,或者已经有
PTHREAD_THREAD_MAX个线程存在于系统中,且都处于激活状态。这里需要说明的事,线程创建函数pthread_create()发生的错
误,不能用perror()函数获得,因为线程的相关操作不使用errno来表示线程相关的错误。(ps,errno在头文
件中定义为 extern int errno)。
线程的退出
在线程的处理函数中,可以调用pthread_exit()函数结束线程执行,也可以不调用pthread_exit()函数,而只让线程处理程序返回。pthread_exit()的函数原型是:
void pthread_exit(void * retval);
pthread_exit()函数没有返回值,其参数是指向线程的返回值,一般需要提前非配空间。retival可以是NULL,表示创建线程者对被创建线程的返回值不感兴趣。
pthread_exit()
函数在返回值前可以调用预先定义的线程退出清理函数。清理函数可以是多个,这些函数的调用次序类似于对栈的数据处理,先注册(使用
pthread_cleanup_push()函数进行注册)的清理函数后执行。清理函数可以在线程退出的时侯,对某些资源如内存、信号量、互斥锁等资源
进行释放,或者进行某些其他的清理工作。
这里需要说明的是,线程的退出不能简单的使用exit()函数,因为一旦使用exit(),实际上就是整个进程的退出,从而导致其他线程也随着进程的消亡而消亡,所以一般情况下是不允许的。
线程可以调用pthread_exit()函数显式的退出,也可以用pthread_cancel()函数终止其他线程的执行。pthread_cancel()的函数原型是:
int pthread_cancel(pthread_t thread);
其中,调用pthread_cancel()函数的线程向另一个线程发送终止执行请求的时侯,需要获得另外一个线程的标志,也就是参数thread。
等待线程的结束
子线程创建完毕后,父线程就可以使用pthread_join()函数等待被创建的字线程结束。
pthread_join()函数会挂起创建线程的执行,直到等待到想要等待的子线程。pthread_join()函数的函数原型:
int pthread_join(pthread_t thread,void ** thread_return);
其中,第一个参数thread是需要等待的线程的标志。如果thread_return不空,那么thread_return不为空,那么
thread_return指向thread返回的值。thread的返回值或者是给pthread_exit()函数传递的参数,或者是
PTHREAD_CANCELED,如果thread是调用pthread_cancel()函数停止执行的线程标志。在调用
pthread_join()函数前,需要等待的线程必须在可加入的状态,也就是说不能在对线程thread调用pthread_detach()函数或者是用pthread_create()函数创建线程thread的时侯将其属性设置为PTHREAD_CREATE_DETACHED。那么为什么要对被创建的进程调用pthread_join()函数呢?原因是当一个处于可加入状态的线程结束的时侯,这个线程的内存资源,包括线程描述符和其使用的栈并不自动释放,而是需要另一个线程(比如创建该线程的线程)来使用pthread_join()函数释放相关的内存资源。可以想象,如果在编写的多线程程序中,没有及时的清理和释放相关内存资源,可能导致当前执行的程序不能继续创建线程,或者由于内存资源的使用超出了规定范围,出现严重的内存泄漏问题。
一般情况下,如果一个线程已经调用pthread_join()函数等待线程的结束,而另一个线程也要使用pthread_join()函数等待同一线程的结束时,那么pthread_join()函数将会返回错误。
线程的分离
pthread_join()
函数虽然可以等待被创建的线程的结束,且可以回收被创建的线程的相关资源,但是这个函数的主要缺点是要挂起调用pthread_join()函数的线程。
在某些情况下,用户希望不断地创建子线程,而且希望子线程本身有自我回收内存资源的能力,POSIX线程库提供了一个由这种功能的函数
int pthread_detach(pthread_t thread);
这个函数的目的是让线程thread可以处于分离detached状态,当线程处于这种状态的时侯,线程不需要其他线程等待其结束。处于分离状态的线程有能力在自己结束执行的时侯回收相关的内存资源。但是需要注意的是,pthread_detach()函数和pthread_join()函数在一般情况下是不能同时使用的。如果一个线程已经使用pthread_detach()函数且被设置为分离状态,或者再创建线程的时
侯,pthread_create()函数传递的线程属性是PTHREAD_CREATE_DETACHED,那么当再次使用
pthread_join()函数的时侯,pthread_join()函数会返回错误。
获得当前线程标志
使用pthread_self()函数可以获得当前线程的标志,pthread_self()函数的返回值就是当前线程的标志,其函数原型是:
pthread_t pthread_self(void)
阅读(1147) | 评论(0) | 转发(2) |