POSIX线程基本概念
多数Linux编程教材里面对POSIX线程的介绍占的篇幅并不多。Linux的fork对多进程的有较好的优化技术,而__clone系统调用相当于使 用进程的方法实现线程。进程的另一个好处是稳定性,进程在退出时操作系统自动回收资源,而共享资源的线程需要程序员自己设计释放资源,积累的错误可能会使 得程序崩溃。
线程和进程如何使用的关系,在不少unix尤其是linux编程的相关社区依然是经久不息的话题。这篇文章大致说了一下linux编程为什么多用进程而少用线程。而Gentoo创始人Daniel Robbins的这一篇相当好的线程入门文章。还有这篇。
我只是初学者,下面是我的学习笔记。
1、__clone函数调用
|
Linux可以通过__clone函数调用创建子进程来实现线程,__clone同时也可以实现fork。__clone的第一个参数是执行子进程时的调 用函数指针,这个函数用一个void类型的指针作为参数,返回int整型;第二个参数为为子进程分配的堆栈指针,如果想让操作系统为它来分配,则设置为 NULL,第三个为CLONE标志,作为调用选项,flags包括:CLONE_VM(是否设置则共享内存镜像,否则就类似于fork,子进程拷贝一份父 进程的副本)、CLONE_FS(是否共享相同的根文件系统,当前动作目录和umask)、CLONE_FILES(是否共享文件描述符,否则子进程依然 会继承父进程的文件描述符表,但它们的读/写/打开/关闭操作互不影响)、CLONE_SIGHAND(是否共享设置在父进程上的信号处理器,否则子进程 自己拷贝一份副本,它们的sigaction互不影响)、CONE_PID(子进程是否继续使用父进程的pid)。最后一个参数arg可以设置为传递 给*fn子函数的参数,注意类型是void,这意味着它是什么类型可以自己选择,包括NULL。
2、POSIX线程库的pthread API
pthread_create:创建一个线程
|
第一个参数为要创建的线程结构pthread_t,第二个参数为线程的相关属性,第三个参数是该线程要执行的函数的指针,第四个则是传递给*start_routine的参数,可以自行选择它的取值和用法。
pthread_create执行成功返回0,并把tid线程标识符存放到pthread_t结构中;否则返回一个非零值并设置errno。
pthread_exit:终止当前线程
|
pthread_exit退出当前线程,退出之前将调用pthread_cleanup_push。它在线程的最上层函数的最后是被隐式调用的,这时可以 加一个retval参数显式调用之,以供pthread_join参考。
pthread_join:挂起当前线程
|
pthread_join把当前线程挂起直到指定的线程th终止,thread_return为th终止时的参数,如果没有显式指定,则为NULL。
pthread_cancel:撤消一个线程
|
pthread_cancel用于撤消线程thread。
pthread_setcancelstate可以用于设置当前线程的撤消状态,包括PTHREAD_CANCEL_ENABLE和 PTHREAD_CANCEL_DISABLE,允许或者忽略撤消。在一些关键操作中可以设置它以避免被另外一个线程用pthread_cancel来撤 消。
pthread_setcanceltype设置当前线程的撤消类型,包括PTHREAD_CANCEL_ASYNCHRONOUS立即撤消、 PTHREAD_CANCEL_DEFERRED延迟至撤消点。可以通过调用pthread_testcancel或者使用条件变量来设置撤消点。
3、线程属性
线程属性包括:
detachstate ——分离或者切入状态,它的值有PTHREAD_CREATE_JOINABLE(默认值), PTHREAD_CREATE_DETACHED;
schedpolicy ——调度策略,取值有SCHED_OTHER(默认值), SCHED_FIFO, SCHED_FIFO;
schedparam ——跟调度策略有关;
inheritsched ——PTHREAD_EXPLICIT_SCHED(默认值), PTHREAD_INHERIT_SCHED;
scope —— 时间片,取值有PTHREAD_SCOPE_SYSTEM(默认值),每个线程一个系统时间片,PTHREAD_SCOPE_PROCESS线程共享系统时间片。
线程属性对象的类型为pthread_attr_t,通过pthread_attr_xxxxx函数族操作线程属性对象。
4、pthread cleanup宏
pthrad cleanup宏主要用于处理线程的退出状态,pthread_exit和pthread_join等可以用它来作参数,包括
|
这些宏主要用于线程结束时释放有关资源,用pthread_exit调用pthread_cleanup_push进行处理时,要注意用完后要用对应的pthread_cleanup_pop把它从堆栈弹出。
5、互斥mutex
由于线程是共享资源的,所以互斥就显得相当重要。互斥提供了对互斥对象上锁的方法以获得对此资源的独占,其它企图对此互斥加锁的线程则会被阻塞而挂起,直到对资源加锁的线程解锁为止。
在使用线程编程时,不必处处都使用互斥,否则会失去并发线程的意义,例如,在对使用只能串行单独访问的资源下方使用互斥。
互斥对象在pthead.h中定义为pthread_mutex_t。它的系统调用主要包括:
|
pthread_mutex_init创建一个互斥对象,并用指定的属性初始化这个互斥对象,初始化属性包 括:PTHREAD_MUTEX_INITIALIZER创建快速互斥,这种互斥执行简单的加锁和解锁,在加锁后阻塞另一个要给它上锁的线 程;PTHREAD_RECURSIVE_MUTEX_INITIALIZER创建递归互斥,这种互斥将给加锁计数,解锁时需要调用同样次数的 pthread_mutex_unlock;
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER创建检错互斥,这种互斥在被锁上后会向试图给它加锁的线程返回一个EDEADLK错误代码而不阻塞。
pthread_mutex_lock和pthread_mutex_unlock则为加锁和解锁指定的互斥对 象,pthread_mutex_trylock和pthread_mutex_lock的不同是如果互斥对象已上锁不会被阻塞而是返回一个EBUSY错 误代码,pthread_mutex_destory析构指针mutex并释放相关资源。这几个调用成功都返回0,失败返回一个非零的错误代码。
6、条件变量
线程用条件变量对象来阻塞自己以等待某个指定条件发生,条件对象在<pthread.h>中定义为pthread_cond_t。
|
pthread_cond_init创建一个指向条件变量的指针,并用一个参数初始化该变量,这个参数在Linux下被忽略而使用 PTHREAD_COND_INITIALIZER,而pthread_cond_t则析构该指针并释放相关系统资源。
线程在锁定一个互斥后,如果需要某个条件到来,则应调用等待所需条件,pthread_cond_wait将检查指定的条件,没有则把当前线程挂起,并解 锁指定的互斥,在条件到达后重新锁定该互斥,并唤醒被挂起的线程。pthread_cond_timedwait则为计时等待,使用的abstime这个 参数为和兼容time()返回值的绝对时间,即以经典的UNIX纪元时间1970-01-01起至今的秒数。如果这个时间前等待条件未发生,则结束等待并 返回ETIMEOUT代码。这两个函数都将被设置为撤消点。
pthread_cond_signal和pthread_cond_broadcast用在一个线程解锁互斥后唤醒等待条件cond的线程,不同之处在 于前者按顺序唤醒第一个进入等待队列的线程,后者唤醒等待该条件的所有线程。