首页 > 编程语言 > 详细

APUE学习笔记——线程和线程同步

时间:2017-03-02 23:29:38      阅读:255      评论:0      收藏:0      [点我收藏+]

1 概念

线程是程序执行流的最小单元。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。

线程包含了表示进程执行环境必须的信息,包括线程ID,一组寄存器值、栈、调度优先级和策略、信号屏蔽字和errno变量以及线程私有数据。

采用多线程的好处:

  • 通过为每种事件类型的处理分配不同的线程,能够简化处理异步事件的代码。
  • 多个进程必须采用复杂的方式才能实现内存和文件描述符的共享,而线程可以访问相同的存储空间和文件描述符。
  • 通过分解成多个线程可以增加程序的吞吐量。
  • 交互的程序依赖于多线程实现响应时间的改善。

1.1 线程标识

每个线程有个线程ID,线程ID只在它所属的进程里有效,用pthread_t数据类型来表示。

#include <pthread.h>
/*进程ID不能直接比较*/ int pthread_equal(pthread_t tid1, pthread_t pid2); /*若相等返回非0值,不等返回0*/
pthread_t pthread_self(void); /*返回自己的线程ID*/

2 线程创建

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void), void *restrict arg); /*成功返回0,否则返回错误编号*/

 

当pthread_create成功返回时,由tidp所指向的内存单元被设置为新创建线程的线程ID。

attr参数用于定制线程的不同属性。

新创建的线程从start_rtn处开始运行,该函数只有一个无类型指针参数arg。

注意:新线程和主线程的执行先后顺序是不确定的。

3 线程终止

如果进程中的任意一个线程调用了exit、_Exit或_exit,那么整个进程就会终止。当信号的默认动作是终止进程,把该信号发送到线程也会终止整个进程。

单个线程可以以下三种方式退出:

  • 线程从启动例程中返回。
  • 线程被同一进程中其他线程取消。
  • 线程调用pthread_exit
#include <pthread.h>

void pthread_exit(void *rval_ptr);//rvta_ptr为线程终止返回值

/*访问其他线程终止的返回值,等待*/
int pthread_join(pthread_t tid, void **rval_ptr);

/*请求取消同一进程中的其他线程,不等待*/
int pthread_cancel(pthread_t tid);

线程可以安排它退出时需要调用的函数

#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg);//清理函数为rtn,参数为arg
void pthread_cleanup_pop(int execute);//如果execute为0,则清理函数将不被调用

线程执行以下动作时调用清理函数

  • 调用pthread_exit时
  • 相应pthread_cancel请求时
  • 用非0参数调用pthread_cleanup_pop时

现在可以看出线程函数和进程函数之间的相似之处:

进程原语 线程原语 描述
fork pthread_create 创建新的控制流
exit pthread_exit 从现有控制流中退出
waitpid pthread_join 从控制流中得到退出状态
atexit pthread_cancel_push 注册在退出控制流时调用的函数
getpid pthread_self 获取控制流ID
abort pthread_cancel 请求控制流的非正常退出

默认情况下,线程的终止状态会保留到对该线程调用pthread_join,如果线程已经处于分离状态,线程的底层资源可以在线程终止时立刻被收回。

/*调用pthread_detach函数使线程处于分离状态*/
#include <pthread.h>
int pthread_detach(pthread_t tid);//成功返回0,失败返回错误编号

当线程被分离时,不能使用pthread_join函数等待它的终止状态。

4 线程同步  

当多个线程共享相同的内存单元时,就容易出现每个线程看到的数据不一致的问题。当多个线程都要修改某个变量时,就需要对它们进行同步,以确保它们在任意的时刻都不会访问到无效的数值。

为了解决这个问题,线程不得不使用锁,同一时间只允许一个线程访问这个变量。

4.1 互斥量 

 互斥量(mutex)本质上是一把锁,在访问共享资源前对互斥量进行加锁,在访问后释放互斥量上的锁。对互斥量加锁之后,如果其他线程试图访问将被阻塞直到当前进程释放该互斥锁。

 互斥变量用pthread_mutex_t数据类型来表示。使用之前必须先进行初始化,可以把它设置为常量PTHREAD_MUTEX_INITIALIZER。如果动态地分配互斥量(例如malloc),那么在释放内存前需要调用pthread_mutex_destory。

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restric mutex, const pthread_mutexattr_t *restrict attr);
/*attr为属性参数,设置attr为NULL则默认参数*/

int pthread_mutex_destory(pthread_mutex_t *mutex);

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);//加锁成功返回0,失败返回EBUSY

int pthread_mutex_unlock(pthread_mutex_t *mutex);

/*若成功返回0,否则返回错误编号*/

如果互斥量已经上锁,线程调用pthread_mutex_lock将阻塞直到互斥量被解锁。如果不希望被阻塞,则调用pthread_mutex_trylock

4.2 避免死锁

 如果线程试图对同一个互斥量加锁两次,就会进入死锁状态。两个线程试图互相锁住对方拥有的互斥量,也会陷入死锁状态。

 可以小心控制互斥量加锁的顺序来防止死锁发生。也可以先释放占有的锁,过一段时间再试。

4.3 读写锁 

 

 

APUE学习笔记——线程和线程同步

原文:http://www.cnblogs.com/alwayswangzi/p/6491087.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!