可移植操作系统接口(Portable Operating System Interface of UNIX),简称POSIX,POSIX标准定义了操作系统应该为应用程序提供的接口标准,由电气和电子工程师协会 ( Institute of Electrical and Electronics Engineers,简称IEEE)定义在各种UNIX操作系统上运行软件使用的一系列API标准的总称。随着操作系统的发展,POSIX标准已经成为信息界一个标准化规范,以提高不同操作系统的兼容性和应用程序的可移植性。不仅仅是适用于UNIX,主流操作系统Linux、Windows、Mac OS已加入POSIX支持,甚至国产实时系统(RTOS)RT-Thread都支持POSIX规范标准。
POSIX线程(POSIX threads),简称Pthreads,是POSIX标准中对线程定义的一部分标准规范。Pthreads定义了创建和操作线程的一整套API(包括类型、函数、常量),在编写程序时,只要遵循Pthreads规范,那么这段程序就是可以在任何支持Pthreads的操作系统上运行,实现优良的跨平台特性。
总的来说,POSIX就是一个标准化规范接口,更详细的POSIX介绍,可以参考文章末尾官方文档。
进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位。不同进程拥有独立的代码空间和内存资源,同一进程下的线程是共享当前进程的所有系统资源。进程和线程存在关联,但又有一定的区别,归纳起来有几个本质的不同点。
从进程与线程的特性差异对比,线程最大的优势是资源开销比进程要小。线程相比进程,还存在几个优点。
注:
线程也存在不足,由于线程是共享进程资源空间,如果一个线程因为异常崩溃导致共享资源(如内存空间)异常,其他线程很可能也会崩溃。进程拥有独立资源空间,以及操作系统的保护机制,一个进程崩溃不会影响其他进程。
Pthreads规范化标准接口,提高应用程序在不同操作系统执行时的兼容性和可移植性,具备优良的跨平台特性。对于程序员来说,可以释放大量的重复编码工作量,提高编码效率。,
Pthreads是优秀的,在通用操作系统上使用,几乎找不到缺点。但是在资源紧张的嵌入式设备上使用时,由于增加代码空间和内存空间的占用,需要增加硬件资源(RAM和ROM),成本上升。如在RT-Thread上支持Pthreads需要占用更多的资源空间,相应的需要更多资源的RAM和ROM支持。
Pthreads定义了一套 基于C 语言的类型、函数和常量,API接口声明位于 pthread.h
头文件中,Pthreads所有 API 以 “pthread_”
为 前缀,命名根据具体功能翻译,比较直观通俗,可读性优良。Pthreads API主要包括线程管理和一系列线程间通信(同步)机制。
提供了线程创建(creating)、分离(detaching)、调度(joining)以及线程属性操作的一系列函数。
提供了通信机制的创建、销毁、访问及它们属性操作的一系列函数。
【1】互斥锁(Mutex)
【2】读写锁(read/write lock)
【4】自旋锁(spin lock)
【5】屏障(barrier)
【6】条件变量(Condition variable)
注:
信号量(semaphore)和消息队列(Message queue)不属于Pthreads标准的范畴内,但属于POSIX的标准。因此,API命名风格上也有差异,信号量API以“sem_”
为前缀,消息队列以“mq_”
为前缀。
本文主要描述线程管理相关的基本操作,函数命名以 “pthread_”
为前缀。
函数组 | 前缀 | 例子 |
---|---|---|
线程属性 | pthread_attr_ | pthread_attr_t |
线程操作 | pthread_ | pthread_create |
大部分 Pthreads 的函数执行成功后返回 0 值,失败则返回一个包含在 errno.h
头文件中的错误代码。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
thread
,线程描述符(ID)attr
,线程属性,如果传入 NULL,则使用默认的线程属性start_routine
,线程任务入口函数arg
,线程私有参数int pthread_detach (pthread_t thread);
thread
,线程描述符注:
一旦线程属性的分离状态设置为 detached,该线程不能被
pthread_join
函数等待或者重新被设置为joinable状态。
int pthread_join (pthread_t thread, void**value_ptr);
value_ptr
,户定义的指针,用来存储被等待线程的返回值地址pthread_join
与pthread_detach
的区别
两者功能类似,都是在线程结束后用来回收线程占用的资源。
一个线程默认属性是可结合状态(joinable), 如果一个线程结束运行但主线程没有等待其结束,则它的状态会变成类似于进程中的僵尸进程状态(Zombie Process),部分资源没有被回收(如退出状态码),所以创建线程者(主线程)应该调用pthread_join
来等待线程结束,获得线程的退出代码,回收资源(类似进程的wait、waitpid
)。
pthread_join
是阻塞函数,直至线程结束,但一些场景下,主线程不能或者不希望被阻塞,此时可以在主线程或者子线程调用pthread_detach
函数,使子线程成为脱离状态(detached),这样该线程结束后会自动释放所有资源,而且pthread_detach
函数是非阻塞函数,主线程也不会被阻塞。
注:
调用pthread_detach
使线程脱离后,再调用pthread_join
,会调用失败,立即返回。
void pthread_exit(void *value_ptr);
value_ptr
,户定义的指针,用来存储线程退出状态的返回值地址 线程的最外层函数的结束可以不用调用pthread_exit
函数来退出线程,因为线程return时,线程会自动退出调度状态,系统会回收线程资源。一般情况下,我们在线程内部,在满足特定条件时,需要退出线程,才调用pthread_exit
使线程终止并退出。
注:
如果多个线程共享一段资源,一个线程退出后,并不会释放共享资源,直至所有线程退出调度,系统才会回收共享资源。
我知道,在使用pthread_create
函数创建线程时,第一个参数保存的就是返回线程ID,为什么Pthreads还需要提供一个获取线程号的函数呢?那是因为,pthread_create
创建线程返回的线程号,前提是主线程必须先执行,否则返回的是一个未知值。因此,线程内获取线程号应该使用pthread_self
函数。
pthread_t pthread_self(void);
例子:线程中将自己分离
pthread_detach(pthread_self());
在POSIX线程中,线程号(ID)的类型用 pthread_t
描述,在不同的系统下, pthread_t
的类型可能不同,比如在Ubuntn中是 unsigned long
类型,在solaris系统中则是 unsigned int
类型,而在FreeBSD中是 pthread_t
。因此,判断线程号是否相等,不能直接使用 “==”
判断,应该使用Pthreads线程比较函数 pthread_equal
检查。
int pthread_equal(pthread_t t0, pthread_t t1);
t0
,线程号1t1
,线程号2#include
#include
#include
#include "pthread.h"
void *thread_entry(void *data)
{
uint32_t count = 0;
printf("thread0 startup\r\n");
for(;;)
{
printf("thread[%lu], run time[%u]\r\n", pthread_self(), count);
if (count >= 5)
{
pthread_exit(NULL);
}
count++;
sleep(1);
}
printf("thread0 exit\r\n");
}
int main(int argc, char **argv)
{
pthread_t thread0;
void *retval;
pthread_create(&thread0, NULL, thread_entry, 0);
pthread_join(thread0, &retval);
return 0;
}
运行结果:
acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/pthread$ ./app
thread0 startup
thread[139872526268160], run time[0]
thread[139872526268160], run time[1]
thread[139872526268160], run time[2]
thread[139872526268160], run time[3]
thread[139872526268160], run time[4]
thread[139872526268160], run time[5]
thread0 exit
注:
编译Pthreads相关应用程序时,需加上Pthreads链接库(“-lpthread”
),如编译“app.c”源码。
gcc app.c -o app -lpthread
【1】POSIX官方文档
【2】POSIX标准介绍