在这里插入图片描述

线程创建

#include <pthread.h>
int  pthread_create((pthread_t *thread, const pthread_attr_t *attr,
                void *(*start_routine)(void*), void *arg);
/********************************
参数:
    thread线程线程标识符
    attr表示线程属性
    start_routine线程函数的起始地址
    arg传递给start_routine的参数
返回值:
    成功返回0,否则返回-1
**********************************/

线程中止

中止方式:1)从启动例程返回;2)被同一进程的其他线程取消;3)线程调用pthread_exit

#include <pthread.h>
void pthread_exit(void *rval_ptr);
//进程中的其他线程可以通过pthread_join访问到rval_ptr指针
int pthread_join(pthread_t thread, void **rval_ptr);

当一个线程通过调用pthread_exit或者从启动例程返回时,进程中别的线程可以通过pthread_join获取该线程退出状态(存在rval_ptr中)。
注:默认情况下线程中止状态会保存直到对该线程调用pthread_join,如果线程已经被分离,则会产生未定义行为,需要调用pthread_detach分离线程,让操作系统在线程退出时收回它所占资源。

线程同步

当一个线程可以修改的变量,其他线程也可以读取或修改,那就需要对这些线程进行同步,确保访问变量存储内容时不会访问到无效值。

避免死锁

1)如果线程试图对一个互斥量加锁两次,会造成死锁;
2)有两个互斥量,第一个线程占有一个,在获取第二个互斥量时阻塞,而第二个线程占有第二个互斥量,获取第一个线程时阻塞,此使也会产生死锁。
解决方法:可以用pthread_tyrlock,如果占有了一个互斥量,并且pthread_trylock成功,那么就继续前进,否则释放第一个互斥量,做好清理工作,再重试。

互斥量(mutex)

在访问共享资源之前对互斥量进行设置(加锁),访问完后释放(解锁)互斥量。对互斥量加锁后,其它想访问互斥量的线程都会被阻塞。

#include <pthread.h>
//成功返回0,否则返回-1
int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

当一个以上的线程需要访问动态分配的对象时,可以在对象中嵌入引用计数(是不是很像shared_ptr),确保所有使用该对象的线程访问完之前不会释放该对象。

pthread_mutex_timedlock

#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *tsptr);

该函数与pthread_mutex_lock基本等价,指定一个线程愿意等待的绝对时间,超时后不会对互斥量加锁而是返回错误码。所以使用该函数可以避免永久阻塞。

读写锁

读写锁比互斥量具有更高的并行性,有三种状态:读模式加锁、写模式加锁和不加锁状态。
一次只有一个线程可以占用写模式的读写锁,但是可以有多个线程占用读模式的读写锁。
适用于对数据结构读的次数远大于写的情况,所以也叫做共享互斥锁。

#include <pthread.h>
//使用前必须初始化,释放内存前必须销毁
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
//读模式锁,rdlock;写模式锁,wrlock
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock((pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock((pthread_rwlock_t *rwlock);
  • 代码实例
    #include <pthread.h>
    #include <stdlib.h>
    struct job{
      ...
      pthread_t j_id;
    };
    struct queue{
      ...
      pthread_rwlock_t q_lock;
    };
    //初始化队列和锁
    int queue_init(struct queue *qp){
      int err;
      ...
      err = pthread_rwlock_init(&qp->q_lock, NULL);
      if(err!=) return err;
      ...  //继续初始化
      return 0;
    }
    //队列中添加、删除任务,设置写锁
    void job_add(struct queue* qp, struct job* jp){
      pthread_rwlock_wrlock(&qp->q_lock);
      ...
      ...
      pthread_rwlock_unlock(&qp->q_lock);
    }
    //队列中查找任务,设置读锁,与写锁类似
    struct job* job_find(struct queue *qp, pthread_t id){
      if(pthread_rwlock_rdlock(&qp->q_lock)!=0)
          return NULL;
      ...
      pthread_rwlock_unlock(&qp->q_lock);
      return jp;
    }

带有超时的读写锁

条件变量

条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。线程在等待条件之前需要先锁住互斥量,才能计算条件。

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);
//等待条件变量变为真,给定的时间内不能满足则返回一个错误代码
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//至少能唤醒一个等待该条件的线程
int pthread_cond_signal(pthread_cond_t *cond);
//能唤醒等待该条件的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);

传递给pthread_cond_wait函数的互斥量对条件进行保护,把锁住的互斥量传递过来会进行解锁,函数返回时再对互斥量加锁。
改变条件状态后,给线程发信号,唤醒等待条件的线程。

  • 代码实例
    #include <stdlib.h>
    //消息的结构体
    struct msg{
      struct msg* next;
      ...
    };
    struct msg* workq;  //工作队列
    //静态初始化
    pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
    //处理消息
    void process_msg(void){
      struct msg *mp;
      for(;;){
          pthread_mutex_lock(&qlock);
          //工作队列为空时,等待信号
          while(workq==NULL)
              pthread_cond_wait(&qready, &qlock);
          mp = workq;
          workq = mp->next;
          pthread_mutex_unlock(&qlock);
      }
    }
    //消息放入工作队列
    void enqueue_msg(struct msg *mp){
      pthread_mutex_lock(&qlock);
      mp->next = workq;
      workq = mp;
      pthread_mutex_unlock(&qlock);
      pthread_cond_signal(&qready);
    }    
    条件是工作队列的状态。把消息放到工作队列中的操作需要占有互斥量,操作结束释放互斥量,传递信号给等待进程。