linux多线程和锁

第一部分 多线程  
第二部分 互斥锁  
第三部分 条件变量  
第四部分 读写锁  
第五部分 自旋锁  
第六部分 线程壁垒  
第七部分 记录锁

 

  
第一部分 多线程
  
Linux 线程的创建: 
int pthread_create ( pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg); 
tid:返回的线程id 
attr:创建线程的属性可以为NULL,也可以在运行时再改变 
func:线程运行的callback函数 
arg:传给callback函数的参数 
主要属性简要说明: 
PTHREAD_CREATE_DETACH :表示同步脱离,且在退出时自行释放所占用的资源, 此时线程不能用pthread_join()来同步,这个属性可以在线程运行时调用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE(默认属性)状态。 
int pthread_detach (pthread_t tid); 
说明:线程默认终止的时候资源不释放需要主进程调用pthread_join函数才能释放(类似于僵死进程和waitpid函数), pthread_detach 使得线程终止即释放资源,主进程不需要调用pthread_join函数 
  
线程退出情形: 
1) 正常return退出 
2) 被其它线程调用pthread_cancel取消 
3) 调用pthread_exit 退出,其中参数为函数退出时返回的数据 
int pthread_join (pthread_t thread, void **rval_ptr); 
阻塞直到指定线程终止, 并返回线程的返回值。 
注意pthread_join函数操作的线程必须不是DETACHED线程。 
rval_ptr返回值设置: 
1) 如果线程正常return,则设置为return的值 
2) 如果线程被取消则设置为PTHREAD_CANCELED 
3) 如果线程调用pthread_exit退出则为pthread_exit 的参数 
退出函数设置: 
当线程退出时,可以设置一组退出函数类似于进程的atexit 
void pthread_cleanup_push (void (*rtn)(void *),   void *arg); 
void pthread_cleanup_pop (int execute); 
退出函数执行条件: 
1) 线程调用pthread_exit 退出 
2) 线程被其它线程取消 
3) 调用pthread_cleanup_pop 并且参数非0 
注意:函数正常return 不调用退出函数 
  
线程的取消: 
int pthread_cancel (pthread_t tid);发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止 . 
int pthread_setcancelstate (int state, int *oldstate) 
设置本线程对cancel信号的反应. 
state有两种值: 
1)PTHREAD_CANCEL_ENABLE(缺省)收到信号后设为CANCLED状态2)PTHREAD_CANCEL_DISABLE,忽略CANCEL信号继续运行 
old_state如果不为NULL则存入原来的cancel状态以便恢复。 
int pthread_setcanceltype (int type, int *oldtype) 
设置本线程取消动作的执行时机,仅当cancel状态为Enable时有效 
type由两种取值: 
1) PTHREAD_CANCEL_DEFFERED表示收到信号后继续运行至下一个取消点再退出 
2) PTHREAD_CANCEL_ASYCHRONOUS表示立即执行取消动作(退出); 
oldtype如果不为NULL则存入原来的取消动作类型值。 
void pthread_testcancel (void) 
检查本线程是否处于canceld状态,如果是,则执行取消动作,否则直接返回。 
此函数用来设置取消点

  
线程私有数据(Thread-specific Data,或TSD) 
int pthread_key_create (pthread_key_t *keyptr, void (*destructor) (void *value)); 
该函数从TSD池中分配一项,将其值赋给keyptr供以后访问使用。如果destr_function不为空,在线程退出(pthread_exit())时将以key所关联的数据为参数调用destr_function(),以释放分配的缓冲区。 
int pthread_key_delete (pthread_key_t key)这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数,而只是将TSD释放以供下一次调用pthread_key_create()使用。 
TSD的读写: 
int pthread_setspecific (pthread_key_t key, const void *pointer) 
写入,将pointer的值(不是所指的内容)与key相关联 
void * pthread_getspecific (pthread_key_t key) 
读出函数,将与key相关联的数据读出来。 
说明:不同线程对同一个key的访问互不冲突。 

  
pthread_once_t initflag = PTHREAD_ONCE_INIT; 
int pthread_once (pthread_once_t *initflag, void  (*initfn)(void)); 
多个线程调用,保证仅执行一次的函数,其中initflag不可以是局部变量。 
获得线程本身id 
pthread_t pthread_self (void); 
线程id 比较int pthread_equal (pthread_t tid1, pthread_t tid2); 相等返回非0 
  
线程IO操作: 
如果多个线程并发访问同一个文件的时候可以使用pread或pwrite它们保证了原子操作 
其它: 
pthread_kill_other_threads_np() 强行杀死所有线程,它没有通过pthread_cancel()来终止线程,而是直接向管理线程发“进程退出”信号,使所有其他线程都结束运行,而不经过cancel动作,当然也不会执行退出回调函数 ,一般用在exec执行前来结束所有正在运行的线程。 
线程属性操作(略) 

  
第二部分 互斥锁
  
Mutex基本操作函数: 
初始化方法: 
 1)  如:static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER 使用默认属性 
 2)  调用pthread_mutex_init 方法,可以同时设定属性 
销毁方法: 
int pthread_mutex_destroy (pthread_mutex_t *mutex); 
销毁一个互斥锁即释放它所占用的资源,并且要求锁当前是非锁定状态。由于在Linux中,互斥锁并不占用任何资源,它除了检查锁状态以外(锁定状态则返回EBUSY)没有其它动作。 
互斥锁基本操作: 
int pthread_mutex_lock (pthread_mutex_t *mutex); 
int pthread_mutex_trylock (pthread_mutex_t *mutex); 
int pthread_mutex_unlock (pthread_mutex_t *mutex); 
  
Mutex基本属性操作: 
int pthread_mutexattr_init (pthread_mutexattr_t *attr); 
int pthread_mutexattr_destroy (pthread_mutexattr_t   *attr); 
int pthread_mutexattr_getpshared (const   pthread_mutexattr_t * restrict attr, int *restrict   pshared); 
int pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared); 
说明: 
1) Mutex默认只能用于多线程间互斥操作即属性为PTHREAD_PROCESS_PRIVATE ,如果用于多进程操作必须修改其属性为PTHREAD_PROCESS_SHARED 
2) pthread_mutexattr_init 和 pthread_mutexattr_destroy 函数用来初始化或析构pthread_mutexattr_t 结构 但由另外两个函数来设置PTHREAD_PROCESS_SHARED 属性 
3) 注意有些平台不支持多进程间互斥锁. 
其它属性简要介绍: 
1) PTHREAD_MUTEX_TIMED_NP: 普通锁(缺省值)。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。 
2) PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争 
3) PTHREAD_MUTEX_ERRORCHECK_NP: 检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁 
4) PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,仅等待解锁后重新竞争。 
  
使用互斥锁注意事项: 
1) Linux 中实现的POSIX线程锁都不是取消点,因此,线程被取消后不会因为收到取消信号而离开加锁等待状态。 
2) 如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,或者设置了异步取消类型,则必须在退出回调函数中解锁。 
3) 这个锁机制同时不是异步信号安全的,所以不应该在信号处理函数中使用互斥锁,否则容易造成死锁。 

  
第三部分 条件变量 
  
条件变量主要的目的是等待直到条件成立。类似于某些系统的Event。 
它主要包括两个动作: 
1)  一个线程等待“条件变量的条件成立”而挂起; 
2) 另一个线程使“条件成立”。 
注意:为了防止竞争条件变量的使用总是和一个互斥锁结合在一起 
  
条件变量基本操作函数:初始化方法: 
 1)  如:static pthread_cond_t  cond=PTHREAD_COND_INITIALIZER默认属性 
 2)  调用pthread_cond_init 方法,可以同时设定属性 
销毁方法: 
int pthread_cond_destroy (pthread_cond_t *cond); 
只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。 
等待操作: 
int pthread_cond_wait (pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex) 
int pthread_cond_timedwait (pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout); 
产生信号操作: 
int pthread_cond_signal (pthread_cond_t *cond);激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个 
int pthread_cond_broadcast (pthread_cond_t *cond);激活所有等待线程 
  
属性基本操作(类似于互斥锁) 
int pthread_condattr_init (pthread_condattr_t *attr); 
int pthread_condattr_destroy (pthread_condattr_t   *attr); 
int pthread_condattr_getpshared (const   pthread_condattr_t * restrict attr, int *restrict   pshared); 
int pthread_condattr_setpshared (pthread_condattr_t   *attr, int pshared); 
  
使用条件变量注意事项: 
1) pthread_cond_wait()和pthread_cond_timedwait()都被实现为取消点, 在该处等待的线程将立即重新运行 2) 被取消后它首先获的互斥锁,然后执行取消动作。即互斥锁是保持锁定状态的,因而需要定义退出回调函数来为其解锁。 
使用举例: 
{ 
pthread_mutex_lock (&mutex)); 
pthread_cond_wait (&cond,& mutex); 
pthread_mutex_unlock (&mutex); 

 


第四部分 读写锁 
  
读写锁基本特点: 
1)如果没有线程持有写锁, 那么获得读锁就会成功 
2)如果没有线程持有读锁和写锁,那么获得写锁才会成功 
它常被应用于频繁读而很少修改得情况。 
  
读写锁初始化或销毁: 
static pthread_rwlock_t rwLock = PTHREAD_RWLOCK_INITIALIZER 
int pthread_rwlock_init (pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t  *restrict attr); 
int pthread_rwlock_destroy (pthread_rwlock_t *rwlock); 
获得锁或释放锁操作 
int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock); 
int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock); 
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); 
int pthread_rwlock_tryrdlock (pthread_rwlock_t   *rwlock); 
int pthread_rwlock_trywrlock (pthread_rwlock_t   *rwlock); 
属性设置 
int pthread_rwlockattr_init (pthread_rwlockattr_t   *attr); 
int pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr); 
int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * restrict attr, int *restrict pshared); 
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared); 
  
第五部分 自旋锁
  
自旋锁基本特点: 
1) 在用户空间实现, 不需要做内核切换 
2) 忙-等待,也就是说在获得不了锁的情况下仍然占用CPU资源 
3) 要求关键区域执行时间必须非常得短小,并且不允许睡眠, 如果执行时间超过了其它线程等待的时间片(即时间片用完,发生了线程切换), 则用自旋锁就失去了意义,并空耗了CPU时间, 这种情况不防称为失败的等待
4) 如果失败的等待非常多会严重影响系统的性能。此时就应该选用其它锁 
  
基本操作函数: 
1) 初始化方法: __pshared大于0表示用在多进程间 
  int pthread_spin_init (pthread_spinlock_t *__lock, int __pshared) 
2)销毁方法: 
int pthread_spin_destory (pthread_spinlock_t * __lock) 
3) 锁操作方法: 
int  pthread_spin_lock (pthread_spinlock_t * __lock) 
int  pthread_spin_trylock (pthread_spinlock_t * __lock) 
int  pthread_spin_unlock (pthread_spinlock_t * __lock) 

  
第六部分 线程壁垒 
  
壁垒(barrier)基本说明: 
主要用来等待某些数量的线程在壁垒处集合。在设定的数目达到之后,解锁这些线程,让它们继续运行。 
而pthread_join() 方法是等待线程结束 
  
壁垒(barrier)基本操作函数: 
初始化: count表示必须调用pthread_barrier_wait线程的个数 
int pthread_barrier_init (pthread_barrier_t  *barrier, const pthread_barrierattr_t *attr, unsigned int count); 
销毁函数: 
int pthread_barrier_destroy (pthread_barrier_t *barrier) 
同步点函数: 
int pthread_barrier_wait (pthread_barrier_t *barrier); 
  
第七部分 记录锁
  
记录锁主要用于不同进程间锁定文件,它可以锁定整个文件或部分字节数据 
注意:记录锁是面向进程的,因此不能用于线程间。 
主要操作函数如下: 
int fcntl (int filedes, int cmd, ... /* struct   flock *flockptr */ ); 
其中: 
struct flock 
{ 
  short l_type; /* F_RDLCK(共享读), F_WRLCK(排斥写), F_UNLCK(解锁) */ 
  off_t l_start; /* 相对于l_whence的偏移(字节单位) */ 
  short l_whence; /* SEEK_SET, SEEK_CUR, or SEEK_END */ 
   off_t l_len; /* 锁定长度,0表示锁定到文件结束 */ 
   pid_t l_pid; /* 用于 F_GETLK */ 
}; 
cmd: 
F_GETLK: 获得锁状态 
F_SETLK: 非阻塞获得锁失败返回EAGAIN 
F_SETLKW: 阻塞获得锁, 可被信号唤醒。 
  
记录锁用法说明: 
1) 当进程终止或关闭描述符的时候,锁自动释放。 
 a.   fd1 = open (pathname, ...);        b.  fd1 = open (pathname, ...); 
       read_lock(fd1, ...);                           read_lock(fd1, ...); 
       fd2 = dup(fd1);                                 fd2 = open (pathname, ...) 
       close(fd2);                                        close(fd2); 
2) 当fork子进程的时候,锁不被继承。 
3) 当执行exec的时候锁不释放。设置了close-on-exec的除外 
4) 有Advisory和Mandatory锁 
    系统中默认是Advisory锁, 当一个进程获得锁进行读写的时候,并不能阻止其它进行不去获得锁直接进行读写, 为了保证操作的正确性, 所有操作文件的进程必须按照规则, 首先获得锁 然后才能进行读写。 
   Mandatory 锁保证了当一个进程获得锁进行读写得时候, 其它进行直接读写文件会失败。

你可能感兴趣的:(linux多线程和锁)