现在的位置: 首页 > 综合 > 正文

线程基础–线程控制

2019年04月22日 ⁄ 综合 ⁄ 共 4271字 ⁄ 字号 评论关闭
3.  线程控制

   1). 线程属性
     目标:可以设置 线程的 detached/join 状态,线程栈的大小和最低地址等属性。
     detached/join 状态的区别
     当线程处于 分离状态(detached)时,线程结束时,os立即回收资源。主线程不可以调用pthread_join获取线程退出时的返回值。
    当线程处于 未分离状态(join)时,线程结束时,主线程 调用pthread_join获取线程退出时的返回值, 随后释放该线程资源
        
     a)数据类型 pthread_attr_t
     b)初始化及释放属性结构 pthread_attr_init  pthread_attr_destroy 
        初始化时 会分配内存,所以一定要与 _..destroy 函数相对应
     c)获取或设置线程分离状态 pthread_attr_setdetachstate      pthread_attr_getdetachstate
       有2种可选的状态值:
       PTHREAD_CREATE_DETACHED   分离状态
       PTHREAD_CREATE_JOINABLE   正常状态,可以使用pthread_join来获取状态
     d)应该获取pthread_atr_destroy的返回值,因为使用pthread_attr_init初始化的时候可能分配有内存,如果释放内存失败的话,
       会造成内存泄漏
     e)控制线程栈的空间的大小
       需求:多个线程的栈空间累计超过了进程的可用虚拟地址空间
             线程调用函数的自动变量很多,或者递归很深
       1)管理stackaddr线程属性,管理stacksize线程属性
       pthread_attr_getstack
       pthread_attr_setstack 
       2)获取或设置线程栈的大小
       pthread_attr_setstacksize  系统帮助分配内存,自己不用管
       pthread_attr_getstacksize
       3)线程栈的保护
       默认大小为宏PAGESIZE,但修改了栈属性后,这个值就会变成0
       pthread_attr_getguardsize
       pthread_attr_setguardsize
     f)线程属性-并发度
       pthread_attr_setconcurency
       pthread_attr_getconcurency
 
   2). 同步属性 (线程的同步对象 (例:互斥量、读写锁、条件变量)的属性)
      实现同步的3种方式中的对象的属性
     a)互斥量属性 pthread_mutexattr_t
       1)初始化及释放  pthread_mutexattr_init pthread_mutexattr_destroy
       2)进程共享属性
         获取与设置共享属性 pthread_mutexattr_getpshared   pthread_mutexattr_setpshared
         PTHREAD_PROCESS_PRIVAE 进程内的多个线程可以访问同一个同步对象,默认属性
         PTHREAD_PROCESS_SHARED 多个进程可以共享同一块内存区域  内存共享技术。将互斥量用于进程间对同一内存区访问的同步
          
       3)互斥量类型属性
         目标:设置 同一线程对已上锁的互斥量再次上锁 是否 死锁等属性。
         PTHREAD_MUTEX_NORMAL   标准的互斥量类型,不做错误检查或死锁检查
         PTHREAD_MUTEX_DEFAULT  依赖于操作系统提供到其他类型的映射
         PTHREAD_MUTEX_ERRORCHECK 提供错误检查
         PTHREAD_MUTEX_RECURSIVE (递归锁)允许多次加锁,但是需要解锁对应次数 tmd,这个类型叫做递归锁
         获取与设置互斥量类型属性
         pthread_mutexattr_gettype  pthread_mutexattr_settype
     应用场景
     PTHREAD_MUTEX_RECURSIVE (递归锁
当将现有的单线程接口放到多线程环境中时,使用递归锁
     例:
     
     

      同一线程中 func1、 func2分别对 互斥量加锁, 若不是用 递归锁则会出现死锁。
    
 
     b)读写锁属性 pthread_rwlockattr_t
        可以设置是否支持进程共享属性 (默认为 只支持 线程共享属性 )
     c)条件变量属性pthread_condattr_t
        可以设置是否支持进程共享属性 (默认为 只支持 线程共享属性
 
4. 线程重入
     可重入函数--指在信号处理函数中,正在执行的程序被信号处理程序中断后,返回时 不能正确运行的函数。
    原因:(a)使用了静态数据结构--全局的,会被其他线程/信号处理函数 等 使用
     (b)调用了 malloc / free ( malloc 为它所分配的存储区维护一个链接表,执行信号处理函数时,进程可能正在修改
          该表,导致被破环)
    (c)标准I/O函数--大部分使用了 全局结构变量
     1)线程安全:如果一个函数同一时刻可以被多个线程安全地调用
     2)系统是否支持线程安全函数  sysconf(_POSIX_THREAD_SAFE_FUNCTIONS)
     非线程安全的原因:返回的数据存放在静态的内存缓存区,多个线程调用该函数的时候 会覆盖前面正在使用的区域。
     将其进行可重入,为其分配自己的缓存器,避免被其他线程干扰,可以保证是线程安全的。
     重入后的函数,成为线程安全,并不意味着 它对信号处理程序是可重入的。(请举例,不太懂??)
     3)异步-信号安全:如果函数对异步信号处理程序的重入是安全的
     4)锁文件的3个函数
       flockfile  ftrylockfile   funlockfile
       该锁是递归锁
     5)确保函数在进程里面只被调用一次
       pthread_once_t var = PTHREAD_ONCE_INIT;
       pthread_once(&var, function);
 
5. 线程私有数据
     一种 让存储和查询 数据 与 某个线程相关的 机制。避免 与其他线程 同步访问的问题
     1)需要的数据类型: pthread_key_t
     2)创建私有数据的步骤
        pthread_key_t key;     
       a)pthread_key_create(&key, 清理函数地址) 一般通过pthread_once确保函数只被执行一次,变量只被初始化一次
       b)char* addr = pthread_getspecific(&key)
       c)为addr分配内存 malloc
       d)pthread_setspecific(&key, addr);
       e)pthread_key_delete删除key
       f)线程退出,执行清理函数地址
 
6.  线程取消
     设置pthread_cancle 函数相关的属性项
     1)线程可以被设置为是否可取消
       pthread_setcancelstate(int state, int* oldstate)
     2)pthread_cancel只是一个申请,只有线程到达了取消点才会取消.
     3)延迟取消pthread_testcancel, 适合于没有取消点的函数
     4)设置取消的类型pthread_setcanceltype
 
7.  线程与IO
     pread,pwrite 原子io操作
    例:
    线程A 

    lseek( fd, 300, SEEK_SET);
    read( fd, buf1, 100);
    线程B

    lseek( fd, 700, SEEK_SET);
    read( fd, buf2, 100);
    当 A执行完lseek,B在A调用read之前调用lseek, 最后 两线程 读取同一条记录 (对同一文件操作,后一个 偏移量设置 覆盖了前一个设置)。
    解决方法:
    pread--将偏移量设定和数据读取成为一个原子操作
    线程A
    pread( fd, buf1, 100, 300);
    线程B
    pread( fd, buf2, 100, 700);
8.  线程与信号
 
    每个线程有自己的信号屏蔽字,但是他们共享 
    1)相同的信号处理函数 2)该信号与某函数的绑定,一个信号绑定到某个函数,这个被所有线程共享, 他们只能看到一个
    多个线程公用进程的信号屏蔽机制,除了2种情况以外:
    硬件故障的信号与计时器超时的信号,只递送给某个线程,其它的信号会发送给所有线程
    pthread_sigmask
    sigwait 等待信号发送.  一般操作需要先阻塞信号,sigwait调用会取消信号的阻塞状态,直到新信号到来
    pthread_kill
    sigwait(sigset_t*, int* signo)
    sigwait的参数2表示捕获到的信号值
 
9.  线程与fork
    pthread_atfork,理论内容相当多,过滤掉
    当线程调用fork时,就会为子进程创建整个进程空间的副本。包括从父进程继承的互斥量、读写锁和条件变量等。
    因此,如果父进程包含多个线程,子进程在fork返回后,没有调用exec (原来的地址空间会被丢弃)的话,就需要清理锁状态。
    pthread_atfork( void (*prepare)(void), void (*parent)(void),void
(*child)(void)
);
    可以设置3个锁清理函数。 
    prepare fork 由父进程 在fork创建子进程前调用,任务:获取父进程定义的所有锁。
    parent fork: fork创建子进程后,但在fork返回之前,在父进程中 调用。
    child fork :fork创建子进程后,但在fork返回之前,在子进程中 调用。
 
10.  同一进程的所有线程共享同一个计时器

11.  同一进程的所有线程共享相同的文件描述符

抱歉!评论已关闭.