首页 > 代码库 > 线程基础--同步机制 (1)

线程基础--同步机制 (1)

1.  线程基础
   概念
   1). 线程全称控制线程

   2). 多线程的优势:
      a) 比进程方便,可以共享相同的内存空间及文件描述符
      b) 可以用于多个任务,而这些任务如果用单进程来实现是串行,在多线程里面由于CPU的调度可以实现穿插执行
      c) 用于交互程序,将用户输入输出与其他部分分开,优化性能

   3). 如何知道系统是否多线程pthread(POSIX线程)
      a) #ifdef  _POSIX_THREADS
      b) sysconf(_SC_THREADS)

   4). 一个线程的数据结构
      线程ID,  一组寄存器, 栈, 调度优先级, 信号屏蔽字, errno,  私有数据
      pthread_t 被实现为结构体这种才是可移植的
      但linux 直接这样定义了 typedef unsigned long int pthread_t;

   函数
   1). 线程id比较
        pthread_equal(pthread_t ptd1, pthread_t ptd2)
        获得线程ID pthread_self(void)

   2). 线程创建
     pthread_create(新线程的id,线程的属性,线程执行地址,函数参数)
     **** 新线程和主线程在创建后是竞争运行
     **** linux上是靠clone创建子进程来实现pthread_create, 所以同一个进程里面的线程获取到pid (进程号)可能不同(一个来自当前进程,一个来自子进程)(fedora8是相同的)

   3). 线程终止
     如果任一线程调用了exit,_Exit,_exit,那么整个进程就会终止
     a)从启动函数返回     return 
     b)被其他线程取消     pthread_cancel
     c)线程调用pthread_exit.   主线程调用时,会等待对等线程结束后再结束主线程
       
   4). 线程阻塞
     pthread_join (pthread_t aim_ptd)  阻塞当前线程一直到目标线程退出 类似于进程控制里面的 wait
     不能对分离状态(设置为,或已经是detach状态)的线程使用pthread_join, 会返回失败
     只有一个线程可以对某个线程使用pthread_join

   5). 请求取消某个线程
      pthread_cancel  提出申请,并不等待

   6). 登记线程退出清理函数,类似于atexit登记函数
      pthread_cleanup_push(函数指针,参数)
      除了pthread_exit, 及响应取消请求而退出的时候执行清理函数,也可以调用pthread_cleanup_pop来执行函数。
      线程return退出是不会执行清理函数的
      pthread_cleanup_pop(int execute) execute为0就不执行清理函数,只是删除该处理函数,必须大于0才调用

   7). 分离线程  pthread_detach
      pthread_t       tid;
      pthread_attr_t  attr;
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
      pthread_create(&tid, &attr, THREAD_FUNCTION, arg);
      可以设置线程为默认的detach,好让操作系统在线程结束的时候,回收默认的资源
 
2.  线程同步
 
    原因: 现代计算机体系结构造成了数据不是顺序一致来实现, 还有程序里面的逻辑,不是原子操作而造成的
 
    1). 互斥量  本质上就是多放一块全局条件变量
      pthread_mutex_t 访问前加锁,访问完解锁
 
      使用步骤:
      a)创建并初始化
        静态的pthread_mutex_t 赋值常量 PTHREAD_MUTEX_INITIALIZER
        动态分配的pthread_mutext_t ,需要调用pthread_mutex_init初始化, pthread_mutex_destroy释放
        通过pthread_mutex_init可以指定互斥量的属性,默认属性输入参数null
      b)加锁
        pthread_mutex_lock,如果已经有锁则阻塞到互斥变量解锁
        尝试加锁 pthread_mutex_trylock  加锁不成功会马上返回 EBUSY       
      c)访问数据 
      d)解锁  
        pthread_mutex_unlock 解锁
 
    2). 避免互斥量死锁
       产生的原因: 
       1. 同一个线程对同一个互斥量加锁2次,线程本身会陷入死锁, 
       2. 多个互斥量被多个线程以相反的顺序访问的时候
 
    3). 读写锁 又名 共享-独占锁
      类型:        读锁 写锁
      数据类型: pthread_rwlock_t
      存在条件: 写锁一次最多一个线程占据,读锁可以并行存在多个
 
      加锁条件:
      a)当前锁是写锁,所有其它新加锁都会被阻塞
      b)当前锁是读锁,如果新加锁是读锁,那么可以获得访问权限
      c)当前锁是读锁,如果新加锁是写锁,那么该锁会被阻塞直到所有读锁解锁。
      ****注意为避免长期的读锁占据写的时间,但有写锁进来后,后续的读锁都会被拒绝掉
      ****适合读的次数大于写的次数的情况
 
      初始化及释放:
      pthread_rwlock_init
      pthread_rwlock_destroy
 
      加锁及解锁
      pthread_rwlock_rdlock
      pthread_rwlock_wrlock
      pthread_rwlock_unlock 
 
      尝试加锁
      pthread_rwlock_tryrdlock
      pthread_rwlock_trywrlock
 
    4). 条件变量
      数据类型 pthread_cond_t
      需要和互斥变量一起使用,受到互斥变量的保护
      静态初始化 赋值常量 PTHREAD_COND_INITIALIZER
      动态初始化和释放
      pthread_cond_init pthread_cond_destroy
 
      作用:
      给多个线程提供了一个会合的场所,允许线程以无竞争的方式等待特定的条件发生
 
      pthread_cond_wait 
      目的: 是通过互斥量的保护,将线程加入到某个等待条件的线程队列里面去
      过程: 进入函数前,调用者将锁住的互斥量传给函数
                 函数内部,将线程加入到队列并解锁      //如果条件不满足此时挂起,并释放锁
                 函数返回,再次对互斥量加锁
      即:进入函数先检测,如果条件满足则执行,不满足则阻塞。在阻塞线程之前,pthread_cond_wait 与 pthread_cond_timedwait 先释放与条件相关的互斥量,允许其他线程获取互斥量、改变条件、释放互斥量并向条件变量发送信号。

      pthread_cond_timedwait
          同上,但是有个时间等待限制,时间是绝对值,即具体的时刻,数据类型 timespec
      唤醒等待条件的线程:
      pthread_cond_signal 唤醒等待某个条件的一个线程
      pthread_cond_broadcast 唤醒等待某个条件的所有线程
 
      学习条件变量用法的2个例子
      http://www.cnblogs.com/yuallen/archive/2010/05/18/1738139.html
      http://www.cnblogs.com/hnrainll/archive/2011/05/01/2033903.html