进程在各自在自己的地址空间中运行,进程间通信要通过进程间通信机制实现,但是一个进程的地址空间中可以执行多个线程,这些线程除了共享数据段还共享文件描述符表,用户id组id,和当前工作目录,errno变量。但同一进程中的线程还有其所独有的:线程id、上下文(寄存器、程序计数器、栈指针)、调度优先级、等等。
线程的创建函数:
int pthread_create(pthread_t*thread,const pthread_att_t*attr,void*(*start_routine)(void*),void*arg);
若成功返回0,错误返回错误号,当一个线程调用此函数继续往下执行,新的线程所执行的代码由函数指针start_routine决定,start_routine接受一个通过pthread_creat函数传进来的void*类型参数arg,start_routine返回时此线程结束。别的线程调用pthread_join得到start_routine的返回值。
线程的终止:
1、从线程函数返回(main函数除外)
2、一个线程调用pthread_cancel终止同一进程中的另一线程
3、线程调用pthread_exit(void *retval)终止自己
线程的等待:
int pthread_join(pthread_t thread,void **retval)
调用该函数的线程将挂起等待,直到id为thread的线程终止
查看线程不同终止方式返回,value_ptr所指向的内存的值:
运行结果:
线程的分离:
线程终止后,线程状态一直保留到其他线程调用pthread_join获取状态为止,但线程也可以被置detach状态,这样一旦终止就立刻回收它所占有的资源,而不保留终止状态。
分离函数:int pthread_detach(pthread_t thread);
可结合的线程能被其它线程收回其资源和杀死,被其他线程回收之前,存储器资源不释放,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在终止时由系统自动释放。
调用pthread_join后,如果线程没有运行结束,调用者会被阻塞,但有时当主线程创建多个子线程进行处理,并不像希望调用pthread_join阻塞,这时就可以置线程为分离状态,这样一来线程运行结束后会自动释放资源。
运行结果:(证明分离的线程状态不能调用pthread_join函数)
线程的同步:
多线程的程序,访问冲突很普遍,可以引入互斥锁(Mutex),获得锁的线程可以进行读写修改操作,然后释放锁给其它线程。
A:实现互斥锁的操作:
lock:
movb $0,%al
xchgb %al,mutex
if(al寄存器的内容>0){
return 0;
}else
挂起等待;
goto lock;
unlock:
movb $1 ,mutex
唤醒等待Mutex的线程;
return 0;
其中“挂起等待”和“唤醒等待线程”的实现:每个Mutex有一个等待队列,一个线程要在Mutex上挂起等待,首先要将自己加入到等待队列中,然后置线程为睡眠状态,然后调用调度器函数切换到别的线程,一个线程要唤醒等待队列中的其他线程,只需从等待队列中取出一项,将睡眠状态改为就绪,加入就绪队列。
引起死锁的两种典型情况:
如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁。也可以使用pthread_mutex_trylock调用替代pthread_mutex_lock
B:条件变量
线程间的同步还有这样这种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条
件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执
行
C:Semaphore(信号量)
信号量和Mutex类似,表示可用资源的数量,信号量的数量大于1。
int sem_init(sem_t *sem,int pshared,unsigned int value);value:可用资源数量;pshared=0,表示同一进程的线程同步
int sem_wait(sem_t *sem);semaphore减1,如果调用时已经为0,则挂起等待
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);释放资源,semaphore加1
int sem_destory(sem_t *sem);
D:读写锁
一个读写锁只能有一个写者或多个读者,但不能即有读者又有写者。