线程概念
典型的UNIX进程可以看成只有一个控制线程,一个进程在某一时刻只能做一件事。有了多个控制线程以后,在程序设计时就可以把进程设计成某一时刻能够做不知一件事,每个线程都有各自的独立任务。
每个线程都包含有表示执行环境所必须的信息,其中包括进程中标识线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号和屏蔽字、errno变量以及线程的私有数据。
一个进程所有信息对进程的所有线程都是共享的,包括可执行程序的代码、程序的全局内存、堆内存、栈以及文件描述符。
线程的基本特点:
1、线程是进程的实体,可以作为系统独立的调试和分派基本单位。
2、线程有不同的状态,系统提供了多种线程控制的原语(控制方法)。比如:创建线程、销毁线程。
3、线程不拥有自己的资源(唯一拥有的就是自己的栈空间),只拥有从属于进程的全部资源,所有资源分配都是面向进程的。
4、一个进程中可以有多个线程同时执行,他们可以执行相同的代码,也可以执行不同的代码。
5、同一个进程内的线程都在同一地址空间下活动(0-4G),相对于多进程、多线程的系统开销小,任务切换快。
6、多进程协调工作时需要通信,而多线程间的数据交换不需要依赖类似IPC的特殊通信机制,简单而高效。
7、每个线程拥有自己独立的线程ID、寄存器信息、函数栈等。
8、线程之间也存在优先级。
线程标识
就像每个进程有一个ID一样,每个线程也有一个ID。
进程ID:用pid_t数据类型来表示,是一个非负整数。线程ID:是用pthread_t数据类型来表示,也是一个非负整数。但是可移植的操作系统中,不能把他们作为整数处理。因此必须用一个函数来对两个线程ID及进行比较。
#include<pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
线程可以通过调用pthread_self函数获得自身的线程ID。
pthread_t pthread_self(void);
当线程需要识别以ID作为标识的数据结构时,以上两个函数可以一起使用。
线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
thread:获取线程ID
attr:用于定制各种不同的线程属性,如果为NULL按照默认方式创建线程。
start_routine:线程的入口函数地址
arg:给线程入口函数传递的参数,如果参数不止一个,需要把这些参数放到结构体中,把结构体的地址作为arg参数传入。
线程终止
单个线程可以通过3种方式退出,因此可以在不终止整个进程的情况下,停止它的控制流。
(1)、线程可以从简单地从启动历程返回,返回值是线程的退出码
(2)、线程可以被同一进程中的其他线程取消
(3)、线程调用pthread_exit。
void pthread_exit(void *retval);
retval参数是一个无类型指针,与传给启动历程的单个参数类似。进程中的其他线程(pthread_join)也可以通过调用pthread_join函数访问到这个指针。
int pthread_join(pthread_t thread, void **retval);
返回值:成功为0,失败返回错误编号
可以通过调用pthread_join自动把线程置于分离状态,这样资源就可以恢复。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。
当一个线程通过调用pthread_exit退出或者简单地从启动历程返回,进程中的其他线程可以通过pthread_join函数获得该线程的退出状态。
如果对线程的返回值不感兴趣,那么可以把retval设置为NULL。在这种情况下,调用pthread_join函数可以等待指定的线程终止,但并不获取线程的终止状态。
注意:pthread_creat和pthread_exit函数的无类型指针参数可以传递的值不止一个,这个指针可以传递包含复杂信息的结构的地址,但是这个结构所使用的内存在调用者完成调用以后仍然必须是有效的。
线程取消
int pthread_cancel(pthread_t thread);
注意:线程可以选择忽略取消或者控制如何被取消。pthread_cancel并不等待线程终止,仅仅是提出请求。
int pthread_setcancelstate(int state, int *oldstate);
这个函数用来设置调用者线程是否响应取消操作。
state:
PTHREAD_CANCEL_ENABLE | 允许响应 |
PTHREAD_CANCEL_DISABLE | 取消响应 |
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void *thr_fn1(void* arg)
{
printf("thread1 returning\n");
return ((void*)1);
}
void *thr_fn2(void* arg)
{
printf("thread2 exiting\n");
return ((void*)2);
}
int main()
{
int err;
pthread_t tid1,tid2;
void* tret;
err=pthread_create(&tid1,NULL,thr_fn1,NULL);
if(err!=0)
{
perror("create thread1");
}
err=pthread_create(&tid2,NULL,thr_fn2,NULL);
if(err!=0)
{
perror("create thread2");
}
err=pthread_join(tid1,&tret);
if(err!=0)
{
perror("join false thread1");
}
printf("thread1 exit code %ld\n",(long)tret);
err=pthread_join(tid2,&tret);
if(err!=0)
{
perror("join false thread2");
}
printf("thread2 exit code %ld\n",(long)tret);
exit(0);
}
$./a.out
thread1 returning
thread1 exit code 1
thread2 exiting
thread2 exit code 2
线程同步
临界区,互斥量,事件对象,信号量
线程属性
下篇文章中会详细介绍