12.线程控制

1.线程属性

  • 可以在运行阶段使用sysconf()检查系统对线程属性的支持情况
    int pthread_attr_init(pthread_attr_t *attr);
    int pthread_attr_destroy(pthread_attr_t *attr);
    pthread_attr_setaffinity_np();//CPU affinity mask,有些进程只运行在第一个CPU,排除第一个可能提高效率
    pthread_attr_setschedparam();//优先级
    pthread_attr_setstackaddr();//设置起始/结束地址
    pthread_attr_setdetachstate();//分离线程
    pthread_attr_setschedpolicy();//调度策略:real-time 、循环分时
    pthread_attr_setstacksize();//改变默认栈大小
    pthread_attr_setguardsize();//用以避免栈溢出设置的扩展大小
    pthread_attr_setscope();//设置竞争对手
    pthread_attr_setinheritsched();//是否继承调用线程的属性
    pthread_attr_setstack();//设置线程栈属性
    

2.同步属性

  • 所有属性都需要进行初始化和销毁
    int pthread_..._init(pthread_mutexattr_t *attr);
    int pthread_..._destroy(...);
    
2.1 互斥量属性(值得注意的几个)
  • int pthread_mutexattr_setpshared(…);
    • 只有设置为PTHREAD_PROCESS_SHARED,放在共享内存中的互斥量才可用于进程间的同步,默认为PTHREAD_PROCESS_PRIVATE
  • pthread_mutexattr_setrobust(…)
    • 健壮属性,PTHREAD_MUTEX_STALLED时在进程终止时不采取特别行动,PTHREAD_MUTEX_ROBUST时,pthread_mutex_lock()返回EOWNERDEAD而非0.
  • pthread_mutex_consistent(…)
    • 指明与该互斥量相关的状态在互斥量解锁之前是一致的,不调用它就解锁,那么下一个获取锁的阻塞线程就会获得ENOTRECOVERABLE(attr将不再可用)
  • pthread_mutexattr_gettype(…)
    • 获取互斥量属性类型(PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RESURSIVE、PTHREAD_MUTEX_DEFAULT)
      • 把现有单线程接口放到多线程环境中的时候递归互斥量(PTHREAD_MUTEX_RESURSIVE)是非常有用的(不能修改函数结构而不得已的做法)
2.2 读写锁的属性
  • pthread_rwlockattr_set/getpshared(…)
    • 进程共享属性
2.3 条件变量属性
  • pthread_condattr_get/setpshared(…)
    • 进程共享
  • pthread_condattr_get/setclock(…)
    • 控制pthread_cond_timewait()函数超时参数采用哪个时钟
2.4 屏障属性
  • pthread_barrierattr_get/setpshared(…)
    • 进程共享

3.重入

  • 考虑多个控制线程在同一时间可能调用相同的函数
  • 很多函数非线程安全因为它们返回的数据存放在静态的内存缓冲区中,可通过要求调用者自己提供缓冲区使函数变为线程安全
  • 可以使用下面这些安全版本(page365)
    localtime_r(...);readdir_r(...);
    gmtime_r(...);strerror_r(...);...
    
  • 不加锁版本的标准IO例程:
    int getc_unlocked(FILE *stream);
    int getchar_unlocked(void);
    int putc_unlocked(int c, FILE *stream);
    int putchar_unlocked(int c);
    
    • 除非调用了flockfile(),否则上面的函数尽量不要用

4.线程特定数据

  • 需先创建一个键
    int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));
    
    • 创建的键存储在keyp中,destructor最好是一个析构函数(线程返回或…exit且keyp非空时调用)
  • 取消键与特定数据的联系
    int pthread_key_delete(pthread_key_t keyp);
    
    • 不会激活析构函数
  • 避免创建键时出现冲突的一个方法(每个线程调用pthread_once,系统能保证只初始化一次)
    static void thread_init(void) {
        pthread_key_create(&key, free);
    }
    static pthread_once_t init_done = PTHREAD_ONCE_INIT;
    pthread_once(&init_done, thread_init);
    
  • 关联键和线程特定数据
    ... pthread_set/getspecific(key,...);
    

5.取消选项

  • 线程属性 可取消状态 和 可取消类型影响线程响应pthread_cancel函数调用时的行为。
    int pthread_setcancelstate(int state, int *oldstate);
    
    • 原子操作:设置当前状态为state,原来的状态存储在oldstate
    • 线程的默认可取消状态是PTHREAD_CANCEL_ENABLE,线程将在下一个取消点对所有挂起的取消请求进行处理。
  • 添加取消点
    void pthread_testcancel(void);
    
    • 取消类型没有设置为无效时有效
  • 默认取消类型为推迟取消,可通过下面的函数修改
    int pthread_setcanceltype(int type, int *oldtype);
    
    • type可以是:
      • PTHREAD_CANCEL_DEFERRED
      • PTHREAD_CANCEL_ASYCHRONOUS:异步取消,不需要取消点

6.线程和信号

  • 每个信号都有自己的信号屏蔽字,信号处理是所有线程共享的
  • 进程中的信号是递送到单个线程的,如果信号跟硬件相关,则一般被发送到引起该事件的线程中,其他则发送到任意线程
  • 阻止信号发送:
    int pthread_sigmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);
    
    • 失败时返回错误码,set参数包含用于修改信号屏蔽字的信号集
    • how:
      • SIG_BLOCK,添加信号集到信号屏蔽字中
      • SIG_SETMASK,用信号集替换信号屏蔽字
      • SIG_UNBLOCK,从信号屏蔽字中移除信号集
  • 等待信号出现:
    int sigwait(const sigset_t *restrict set,int *restrict signop);
    
    • sigwait允许把异步产生的信号用同步的方式处理,为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后安排专用线程处理
  • 把信号发送给线程
    int pthread_kill(pthread_t thread, int signo);
    

7.线程和fork

  • 子进程中只存在一个线程,如果父进程中的线程占有锁,子进程将同样占有,但子进程不包含占有锁的线程的副本,所以不知道占有了哪些锁、需要释放哪些锁
    • 在fork返回和子进程调用exec函数之间,子进程只能调用异步信号安全的函数
  • 要清除锁状态:
    int pthread_atfork(void (*prepare)(void), void (*parent)(void),
            void (*child)(void));
    
    • 安装帮助清理锁的函数
  • 程序看不懂…

8.线程和IO

  • 因为进程中的线程共享相同的文件描述符
    ssize_t pread(int fd, void *buf, size_t count, off_t offset);
    ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
    
    • 偏移量的设定和读取成为原子操作
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页