zoukankan      html  css  js  c++  java
  • pthread小结

    参考1 https://computing.llnl.gov/tutorials/pthreads/
    参考2 http://man7.org/linux/man-pages/man7/pthreads.7.html


    join

    • int pthread_join(pthread_t, void**);阻塞调用线程,直至指定pthread_t线程终止
    • 在同一个线程中重复调用join会导致错误
    • 在创建线程的时候可以指定要创建的线程是否joinable,如果是,则可以join,否则(即detached)不可以。一般默认都是joinable
    • POSIX指出线程should指定为joinable
    • 如果确定一个线程需要join,那么最好明确指定该线程joinable,通过如下四步:
    1. Declare a pthread attribute variable of the pthread_attr_t data type
    2. Initialize the attribute variable with pthread_attr_init()
    3. Set the attribute detached status with pthread_attr_setdetachstate()
    4. When done, free library resources used by the attribute with pthread_attr_destroy()
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    for(t=0; t<NUM_THREADS; t++) {
       printf("Main: creating thread %ld
    ", t);
       rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t);  //一个attr可以给多个线程使用
       if (rc) {
          printf("ERROR; return code from pthread_create() is %d
    ", rc);
          exit(-1);
          }
       }
    pthread_attr_destroy(&attr);    //记得释放资源。create执行完之后就可以释放,而不用等待线程结束
    
    • 如果确定一个线程不需要joinable,那么应该明确考虑设置属性为detached
    • 通过 pthread_detach()来设置线程为不可join,即使它被创建的时候被设置为joinable。这个动作不可逆

    stack

    • POSIX没有规定创建的线程的stack大小是多少,这是由implementation决定的
    • pthread_attr_setstacksize 可以用来设置需要的stack大小
    • pthread_attr_getstackaddrpthread_attr_setstackaddr可以用来设置stack需要放置到特定的内存区域
    size_t stacksize;
    pthread_attr_init(&attr);
    pthread_attr_getstacksize (&attr, &stacksize);
    printf("Default stack size = %li
    ", stacksize);
    size = 10000; //设置为10000bytes
    pthread_attr_setstacksize (&attr, stacksize);
    printf("set stack size = %li
    ", stacksize);
    pthread_create(&threads[t], &attr, dowork, (void *)t);
    

    mutex

    Creating and Destroying Mutexes

    //destroy,成功则返回0
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    //动态初始化,成功则返回0. 如果attr为NULL,那么将使用默认属性,相当于PTHREAD_MUTEX_INITIALIZER
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
    //使用默认参数静态初始化
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    //mutex属性
    int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
    int pthread_mutexattr_init(pthread_mutexattr_t *attr);
    
    • 被destroy的mutex可以使用pthread_mutex_init重新初始化
    • destroy一个处于lock状态的mutex,将会导致undefined行为
    • 只有mutex可以用来执行synchronization,用它的copies来执行lock,unlock和trylock将导致undefined
    • 不可以重复初始化已经初始化了的mutex

    Locking and Unlocking Mutexes

    //如果别的线程已经lock,那会一直阻塞当前线程直至获得锁
    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类型 性质
    PTHREAD_MUTEX_NORMAL 对mutex的重复lock,即本线程已经lock了mutex,在没有unlock之前又尝试lock,将导致死锁行为;unlock一个没有被本线程lock或者没有被任何线程lock的mutex,将导致未定义行为
    PTHREAD_MUTEX_ERRORCHECK 尝试重复lock一个mutex将不会死锁,而是返回一个错误值;unlock一个没有被本线程lock或者没有被任何线程lock的mutex,也会返回错误值
    PTHREAD_MUTEX_RECURSIVE mutex可以被重复lock。每次lock会增加相关计数,直至通过unlock使计数达到0时,才可以被别的线程lock;unlock一个没有被本线程lock或者没有被任何线程lock的mutex,也会返回错误值
    PTHREAD_MUTEX_DEFAULT 重复lock会导致未定义行为(NORMAL中会导致死锁);unlock一个没有被本线程lock或者没有被任何线程lock的mutex,也将导致未定义行为。 不过,在NDK的定义中,直接把PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
    • pthread_mutex_trylockpthread_mutex_lock只有一点区别:如果当前mutex被任意线程lock,pthread_mutex_trylock都将会立刻返回。如果mutex是PTHREAD_MUTEX_RECURSIVE的,且mutex已经被当前调用线程lock,pthread_mutex_trylock也同样会导致计数增一,并返回success。
    • 如果正在等待lock的线程收到了一个signal,当其从signal handler返回之后,会继续等待lock,就和signal没有发生一样
    • 除非使用了 thread priority scheduling,否则多个正在等待lock的线程获得lock的情况可能多少有点random
    • 如果成功,这三个函数都是返回0,否则返回相应的error

    Condition Variables

    • mutex通过控制对数据的访问权限来达到同步;而condition variables则基于数据的值来控制同步
    • 如果不使用condition variable,线程想要检查某个条件则只能通过轮询的方式,这将非常resource consuming,因为这期间线程将一直active。而使用condition variable则将在不使用轮询的情况下实现此目标
    • condition variable 经常和mutex一起使用

    Creating and Destroying Condition Variables

    int pthread_cond_destroy(pthread_cond_t *cond);
    int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    int pthread_condattr_destroy(pthread_condattr_t *attr);
    int pthread_condattr_init(pthread_condattr_t *attr);
    
    • destory正由某个线程用于block的cv将导致未定义行为
    • 只有cv自己能够用于同步,任何基于它的copies调用pthread_cond_wait(), pthread_cond_timedwait(), pthread_cond_signal(), pthread_cond_broadcast(), pthread_cond_destroy() 都会产生未定义行为
    • 初始化一个已经初始化的cv会导致未定义行为;已经destory的cv可以再次初始化;

    Waiting and Signaling on Condition Variables

    一般使用流程:
    FueOfg.jpg

    int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, 
                                const struct timespec *restrict abstime);
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
    
    • 这两个函数将导致调用线程block on the condition variable, 并且需要传入一个由调用线程lock了的mutex,否则导致未定义行为
    • 如果在wait之前,没有明确lock对应mutex,可能并不会导致block
    • 这两个函数会原子的unlock the mutex,并且导致调用线程block on the condition variable。这里的原子的意味着:只要其他线程lock了这个mutex,那么这个线程对pthread_cond_broadcast()pthread_cond_signal()的调用都会产生调用wait的线程已经blocked on the condition variable的效果
    • 只要这两个函数返回,那么调用线程就已经lock了这个mutex
    • 虚假唤醒Spurious wakeups 可能会产生,而且这并不违反标准,所以,即使调用线程被唤醒,也不意味着对某个值做出某种保证,应该再次确认条件是否真的满足了。同时,考虑到线程之间的竞争,pthread_cond_timedwait由于超时返回之后,条件也可能已经满足。总之。任何时候wait返回,都需要重新评估条件是否满足,这点非常重要
    • 一旦线程waits on the condition variable,那么这个cv就和相应的mutex绑定了,在wait返回之前,不能再使用另外的mutex来调用wait,这会导致未定义行为
    • condition wait是一个cancellation point未明白
    • 假设一个由于wait调用而block线程由于被canceled而unblocked,这个不会consume任何condition signal。
    • pthread_cond_timedwait()pthread_cond_wait()是equivalent的,除了:当signaled或者broadcasted超过指定时间,pthread_cond_timedwait()就会返回返回error。同时,cv还可以支持 Clock Selection,选择不同的Clock来measure指定的时间
    • 当cv wait期间,一个signal产生了,那么cv可能会继续wait就像没有中断一样,或者这会形成一个spurious wakeup,返回0.
      推荐使用while循环替代if语句来检查当前条件是否真的满足,有如下三点好处:
    1. 如果有多个线程都是在wait相同过的wake up signal,那么当其他任意一个被waked up之后,他们都有可能更改条件值,而导致条件不满足
    2. 线程可能会因为程序bug而收到一个signal
    3. Pthreads library被容许产生虚假唤醒,而且这并不违反标准
    int pthread_cond_broadcast(pthread_cond_t *cond);
    int pthread_cond_signal(pthread_cond_t *cond);
    
    • 当多于一个线程在wait的时候,应该使用pthread_cond_broadcast()
    • 在signal之前,应该先lock对应mutex,然后在signal之后,应该unlock对应mutex。如果忘记了unlock,那么相应的wait线程会继续blocked,因为他们无法获得lock

    结束线程

    有:


    其他函数

    pthread_self

    pthread_t pthread_self(void);返回调用线程的thread id

    pthread_equal

    int pthread_equal(pthread_t t1, pthread_t t2);比较两个ID是否相等,如果相等则返回not-zero value不相等则返回0。由于pthread_t结果opaque,所以不应该用==来比较

    pthread_once

    int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));:在进程中,任何首次调用这个函数的线程,在pthread_once_t once_control = PTHREAD_ONCE_INIT的时候,会调用init_routine程序。并且当此函数返回的时候,init_routine已经执行完了(这里没有说init_routine会阻塞调用线程,可能考虑的是,当线程A已经调用init_routine,而另外一个线程B也调用了pthread_once,那么是否B也会等待A调用的init_routine执行完毕?)。如果成功完成,则pthread_once返回0。如果once_control参数不是PTHREAD_ONCE_INIT,那么行为将是undefined。在LinuxThreads中:

    在LinuxThreads中,实际"一次性函数"的执行状态有三种:NEVER(0)、IN_PROGRESS(1)、DONE (2),如果once初值设为1,则由于所有pthread_once()都必须等待其中一个激发"已执行一次"信号,因此所有pthread_once ()都会陷入永久的等待中;如果设为2,则表示该函数已执行过一次,从而所有pthread_once()都会立即返回0。

    这个函数在当无法编辑进程的main函数,比如写一个库的时候,就很有用。
    TODO:如果多个线程使用的init_routine不相同怎么办?或者比如自己开发库,但是user的main中已经使用不同的init_routine调用了pthread_once,那么会是什么结果?


  • 相关阅读:
    firefox上网问题解决
    ubuntu内核的编译安装
    ubuntu常用命令
    source insight 添加文件类型
    ubuntu版本查看命令
    百年孤独与拉丁美洲历史--转载
    Guess Number Higher or Lower II--困惑
    Symmetric Tree
    一棵开花的树
    yii2 mysql数据库读写分离配置
  • 原文地址:https://www.cnblogs.com/willhua/p/10053769.html
Copyright © 2011-2022 走看看