pthread库和stl库

一、pthread库

1.1 线程相关函数

1.1.1 线程的创建、退出和回收

线程创建
int pthread_create(pthread_t* tidp, const pthread_attr_t* attr, void* (*start_rtn)(void*), void* arg);

 args:

  • tidp:新创建线程的线程ID。
  • attr:用于定制各种不同的线程属性。通常直接设为NULL。
  • start_rtn:新创建线程从此函数开始运行。线程运行函数的起始地址
  • (4)arg:start_rtn函数的参数。无参数时设为NULL即可。有参数时输入参数的地址。当多于一个参数时应当使用结构体传入。运行函数的参数

return:

    若线程创建成功,则返回0。若线程创建失败,则返回出错编号。返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于指定各种不同的线程属性。

线程退出
void pthread_exit(void *retval);    // retval参数返回的地方
int pthread_cancel(pthread_t thread);
// 子线程return退出
  • pthread_exit
    • 退出线程,返回参数(如果有)
    • 不影响其他线程,即使是主线程,退出不影响子线程。
  • return
    • 退出线程,返回参数,如果主线程return,子线程也停止。
  • pthread_cancel
    • 向指定线程发送终止信号。
    • 发送成功并不意味着thread会终止,至于目标线程是否接收该信号,何时响应该信号,全由目标线程决定。
线程资源回收
int pthread_join(pthread_t thread, void **retval);
int pthread_tryjoin_np(pthread_t thread, void **retval);
int pthread_timedjoin_np(pthread_t thread, void **retval, const struct timespec *abstime);
int pthread_clockjoin_np (pthread_t *thread, void **thread_return, clockid_t clockid, const struct timespec *abstime)
  • pthread_join
    • 阻塞函数等待回收指定线程的资源
    • 参数retval用于接收线程的返回值。如果不需要,设置为NULL
    • 调用该函数的线程将等待参数thread所标识的线程的退出
    • 参数thread所标识的线程的资源在调用pthread_join时释放
  • pthread_tryjoin_np
    • 该函数执行非阻塞的链接。
    • 如果调用该函数时,参数thread指定的线程已终止,则其功能与pthread_join相同
    • 如果调用该函数时,参数thread指定的线程尚未终止,则该函数以错误的方式立即返回
  • pthread_timedjoin_np
    • 带超时时间的链接。
    • 如果线程尚未终止,且线程在参数abstime指定的时间内结束,则其功能与pthread_join相同
    • 如果超时在线程终止前到期,则函数返回调用超时错误。
  • pthread_clockjoin_np
    • 和pthread_timedjoin_np一样,只是时间不一样。
线程状态分离
int pthread_detach(pthread_t thread);

从状态上实现线程分离,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程),其退出状态不由其他线程获取,而直接自己自动释放(自己清理掉PCB的残留资源),unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己。

其他函数
pthread_t pthread_self (void);   // 获取线程ID。
int pthread_equal(pthread_t t1, pthread_t t2);    // 比较两个线程ID是否相等.

1.1.2 pthread_attr属性设置

线程属性
typedef struct{
       int                       detachstate;   // 线程的分离状态
       int                       schedpolicy;   // 线程调度策略
       structsched_param         schedparam;    // 线程的调度参数
       int                       inheritsched;  // 线程的继承性
       int                       scope;         // 线程的作用域
       size_t                    guardsize;     // 线程栈末尾的警戒缓冲区大小
       int                       stackaddr_set; // 线程的栈设置
       void*                     stackaddr;     // 线程栈的位置
       size_t                    stacksize;     // 线程栈的大小
}pthread_attr_t;
默认属性初始化和属性销毁
int pthread_attr_init (pthread_attr_t* attr);
int pthread_attr_destroy (pthread_attr_t* attr);
  • pthread_attr_init:初始化一个线程属性对象,所包含的内容就是操作系统实现支持的线程所有属性的默认值。
  • pthread_attr_destroy:销毁一个线程属性对象。
线程状态属性
int pthread_attr_getdetachstate (const pthread_attr_t *__attr, int *__detachstate)
// 例如:int detachstate;
//      pthread_attr_getdetachstate(&attr, detachstate);

int pthread_attr_setdetachstate (pthread_attr_t *__attr, int __detachstate);
// 例如:pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
线程栈警戒缓冲区
int pthread_attr_getguardsize (const pthread_attr_t* attr, size_t* guardsize);
int pthread_attr_setguardsize (pthread_attr_t* attr, size_t guardsize);
线程调度策略
int pthread_attr_getschedpolicy (const pthread_attr_t* attr, int* policy);
int pthread_attr_setschedpolicy (pthread_attr_t* attr, int policy);

程的调度策略,有SCHED_FIFO、SCHED_RR和SCHED_OTHER,默认的任务是CFS调度器。

 

线程调度优先级
int pthread_attr_getschedparam (const pthread_attr_t* attr, struct sched_param* param)
int pthread_attr_setschedparam (pthread_attr_t* attr, const struct sched_param *param)

struct sched_param{
    int sched_priority; // 参数的本质就是优先级
}

设置线程优先级,仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效。sched_priority 从 1 到 99。

线程间cpu竞争范围
int pthread_attr_getscope (const pthread_attr_t* attr, int* scope)
int pthread_attr_setscope (pthread_attr_t* attr, int scope)

表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEMPTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

线程栈地址和大小
int pthread_attr_getstacksize (const pthread_attr_t* attr, size_t* stacksize);
int pthread_attr_setstacksize (pthread_attr_t* attr, size_t stacksize);

int pthread_attr_getstack (const pthread_attr_t* attr, void** stackaddr, size_t* stacksize);
int pthread_attr_setstack (pthread_attr_t* attr, void* stackaddr, size_t stacksize);
线程CPU亲和集
int pthread_attr_setaffinity_np (pthread_attr_t* attr, size_t cpusetsize, const cpu_set_t* cpuset);
int pthread_attr_getaffinity_np (const pthread_attr_t* attr, size_t cpusetsize, cpu_set_t* cpuset);

设置arrt中的CPU亲和性,它允许将线程绑定到特定的 CPU 核心或 CPU 集合上,以控制线程在哪些 CPU 上运行。

  • cpusetsize:CPU 亲和性集合的大小(以字节为单位)。
  • cpuset:指向 CPU 亲和性集合的指针,其中包含要绑定线程的 CPU。
线程默认属性设置获取
int pthread_getattr_default_np(pthread_attr_t *attr);
int pthread_setattr_default_np(pthread_attr_t *attr);

1.1.3 线程的属性

int pthread_getattr_np(pthread_t thread,pthread_attr_t *attr);

获取指定线程(已经运行的)ID的属性。

int pthread_setschedparam (pthread_t target_thread, int policy, const struct sched_param* param);
int pthread_getschedparam (pthread_t target_thread,int* policy, struct sched_param* param);

指定线程ID的调度策略和相关参数。可以设置已经存在的线程。

int pthread_setschedprio (pthread_t target_thread, int prio);

    用来设置线程调度的优先级,优先级范围是1~99,注意:这个函数需要在特权进程中使用,并且要求是SCHED_FIFO、SCHED_RR调度策略。如果线程正在运行或运行,就绪队列中的位置对其的影响取决于修改的方向:如果你提高优先级,线程成为优先级的就绪队列的尾部。如果你不改变优先级,线程不会改变位置在就绪队列中。如果你降低优先级,线程成为这个优先级就绪队列的头。

int pthread_setname_np(pthread_t thread, const char* name);
int pthread_getname_np(pthread_t thread, char* name, size_t len);

设置/获取线程的名称,默认情况下,使用pthread_create()创建的所有线程都继承程序名称。

int pthread_getconcurrency(void);
int pthread_setconcurrency(int level);

    set/get当前进程的并发度,如果操作系统当前正控制着并发度(即之前没有调用过pthread_setconcurrency函数),那么pthread_getconcurrency将返回0。thread_setconcurrency函数设定的并发度只是对系统的一个提示,系统并不保证请求的并发度一定会被采用。如果希望系统自己决定使用什么样的并发度,就把传入的参数level设为0

1.1.4 线程控制函数

int pthread_yield (void)

pthread_yield 使当前的线程自动放弃剩余的CPU时间从而让另一个线程运行(就和sleep差不多)

int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,const cpu_set_t *cpuset);
int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset);

 指定线程绑定到特定CPU集。

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

pthread_once的作用为,在给定once_control的情况下,在整个程序中仅调用一次init_routine(在多线程中具体哪一个线程执行不一定)。如果再次调用,pthread_once将不会调用init_routine。(当然也可以使用单例模式,不过要注意,单例对象必须以local static形式存在,因为non-local static 变量的初始化顺序存在不确定性,而 local static 变量的初始化时机是可知的。)pthread_once_t类型的变量,并将其初始化为一个特定的值(通常是PTHREAD_ONCE_INIT)。这个值用于标识初始化状态。

int pthread_setcancelstate(int state, int *oldstate)  

设置本线程对Cancel信号的反应。 state有两种值:pthread_CANCEL_ENABLE(缺省)和pthread_CANCEL_DISABLE,分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state 如果不为NULL则存入原来的Cancel状态以便恢复。

  • 如果目标线程的cancel state是PTHREAD_CANCEL_ENABLE(默认),取消请求会到达目标线程。
  • 如果目标线程的cancel state是PTHREAD_CANCEL_DISABLE,取消请求会被放入队列。直到目标线程的cancel state变为PTHREAD_CANCEL_ENABLE,取消请求才会从队列里取出,发到目标线程。
int pthread_setcanceltype (int type, int* oldtype);

取消请求到达目标线程后,根据目标线程的cancel type来决定线程何时取消:

  • 如果目标线程的cancel type是PTHREAD_CANCEL_DEFERRED(默认),目标线程并不会马上取消,而是在执行下一条cancellation point的时候才会取消。有很多系统函数都是cancellation point,详细的列表可以在Linux上用man 7 pthreads查看。除了列出来的cancellation point,pthread_testcancel()也是一个cancellation point。就是说目标线程执行到pthread_testcancel()函数的时候,如果该线程收到过取消请求,而且它的cancel type是PTHREAD_CANCEL_DEFERRED,那么这个线程就会在这个函数里取消(退出),这个函数就不再返回了,目标线程也没有了。
  • 如果目标线程的cancel type是PTHREAD_CANCEL_ASYNCHRONOUS,目标线程会立即取消(这里的“立即”只是说目标线程不用等执行到属于cancellation point的函数的时候才会取消,它会在获得调度之后立即取消,因为内核调度会有延时,所以并不能保证时间上的“立即”)。
int pthread_getcpuclockid (pthread_t thread_id, clockid_t* clock_id);
// 获取指定线程的cpu时间
int pthread_atfork (void (*prepare) (void), void (*parent) (void), void (*child) (void));

在父进程调用fork函数派生子进程的时候,如果父进程创建了pthread的互斥锁(pthread_mutex_t)对象,那么子进程将自动继承父进程中互斥锁对象,并且互斥锁的状态也会被子进程继承下来:如果父进程中已经加锁的互斥锁在子进程中也是被锁住的,如果在父进程中未加锁的互斥锁在子进程中也是未加锁的。在父进程调用fork之前所创建的pthread_mutex_t对象会在子进程中继续有效,而pthread_mutex_t对象通常是全局对象,会在父进程的任意线程中被操作(加锁或者解锁),这样就无法通过简单的方法让子进程明确知道被继承的 pthread_mutex_t对象到底有没有处于加锁状态。

该函数通过3个不同阶段的回调函数来处理互斥锁状态。参数如下:

  • prepare:将在fork调用创建出子进程之前被执行,它可以给父进程中的互斥锁对象明明确确上锁。这个函数是在父进程的上下文中执行的,正常使用时,我们应该在此回调函数调用 pthread_mutex_lock 来给互斥锁明明确确加锁,这个时候如果父进程中的某个线程已经调用pthread_mutex_lock给互斥锁加上了锁,则在此回调中调用 pthread_mutex_lock 将迫使父进程中调用fork的线程处于阻塞状态,直到prepare能给互斥锁对象加锁为止。
  • parent: 是在fork调用创建出子进程之后,而fork返回之前执行,在父进程上下文中被执行。它的作用是释放所有在prepare函数中被明明确确锁住的互斥锁。
  • child: 是在fork返回之前,在子进程上下文中被执行。和parent处理函数一样,child函数也是用于释放所有在prepare函数中被明明确确锁住的互斥锁。

1.2 mutex相关函数

typedef union
{
  struct __pthread_mutex_s
  {
    int __lock;
    unsigned int __count;
    int __owner;
    unsigned int __nusers;
    int __kind;
    short __spins;
    short __elision;
    __pthread_list_t __list;
  } __data;
  char __size[__SIZEOF_PTHREAD_MUTEX_T];
  long int __align;
} pthread_mutex_t;

1.2.1 mutex的创建回收

mutex创建
pthread_mutex_t mutex_lock=PTHREAD_MUTEX_INITIALIZER;    // 静态初始化
int pthread_mutex_init (pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr);
mutex销毁
int pthread_mutex_destroy (pthread_mutex_t* mutex);

1.2.2 mutex加锁放锁

mutex加锁
int pthread_mutex_trylock (pthread_mutex_t *__mutex);

如果 mutex 所引用的互斥对象当前被任何线程(包括当前线程)锁定,则将立即返回该调用。否则,该互斥锁将处于锁定状态,调用线程是其属主。

int pthread_mutex_lock (pthread_mutex_t *__mutex);

如果这个锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个锁的排队队列中,并会进入阻塞状态, 直到拿到锁之后才会返回。

int pthread_mutex_timedlock (pthread_mutex_t* mutex, const struct timespec* abstime);
int pthread_mutex_clocklock (pthread_mutex_t* mutex, clockid_t clockid,	const struct timespec* abstime);

等待加锁,超时返回。

mutex解锁
int pthread_mutex_unlock (pthread_mutex_t* mutex);

1.2.3 mutexattr属性设置

mutex attr默认初始化和销毁
int pthread_mutexattr_init (pthread_mutexattr_t* attr);

初始化mutex属性,默认属性。

int pthread_mutexattr_destroy (pthread_mutexattr_t* attr)

销毁。

shared属性
int pthread_mutexattr_setpshared(const pthread_mutexattr_t* attr, int pshared);
int pthread_mutexattr_getpshared(pthread_mutexattr_t* attr, int pshared);

设置 pshared属性,pshared取值:

  • PTHREAD_PROCESS_SHAREAD:表示此属性的互斥锁放在共享内存中,可以被多个进程中的线程共享
  • PTHREAD_PROCESS_PRIVATE:表示此属性的互斥锁只能被和创建此锁的线程在同一进程中的其他线程共享(推荐默认值)
type属性
int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type);
int pthread_mutexattr_gettype(pthread_mutexattr_t* attr, int* type);

设置type属性, type的四种取值:

  • PTHREAD_MUTEX_NORMAL:标准类型,不做任何特殊的错误检查或者死锁检测。
  • PTHREAD_MUTEX_ERRORCHECK :提供错误检测。如果在同一个线程里去锁一个还没有解锁的互斥量,会报告错误。
  • PTHREAD_MUTEX_RECURSIVE:递归类型。此互斥量类型允许同一线程在互斥量解锁前对该互斥量进行多次加锁。递归互斥量维护锁的计数,在解锁次数和加锁次数不相同的情况下,不会释放锁,别的线程就无法加锁此互斥量。
  • PTHREAD_MUTEX_DEFAULT
protocol属性
int pthread_mutexattr_getprotocol(const pthread_mutexattr_t* attr, int* restrict protocol);
int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); 

protocol 属性定义在利用互斥对象时要遵循的协议。 protocol 参数的值可以是下列其中一项,在 pthread.h 头文件中定义:

  • PTHREAD_PRIO_NONE:当线程拥有具有 PTHREAD_PRIO_NONE 协议属性的互斥对象时,其优先级和调度不受其互斥对象所有权的影响。
  • PTHREAD_PRIO_INHERIT:此协议值(如 thrd1)会影响线程的优先级和调度。如果更高优先级的线程因 thrd1 所拥有的一个或多个互斥锁而被阻塞,而这些互斥锁是用PTHREAD_PRIO_INHERIT 初始化的,则 thrd1 将以高于它的优先级或者所有正在等待这些互斥锁(这些互斥锁是 thrd1 指所拥有的互斥锁)的线程的最高优先级运行。
  • PTHREAD_PRIO_PROTECT:当线程拥有一个或多个使用 PTHREAD_PRIO_PROTECT 初始化的互斥锁时,此协议值会影响其他线程(如 thrd2)的优先级和调度。thrd2 以其较高的优先级或者以 thrd2 拥有的所有互斥锁的最高优先级上限运行。基于被 thrd2 拥有的任一互斥锁阻塞的较高优先级线程对于 thrd2 的调度没有任何影响。
prioceiling属性
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t* attr, int* restrict prioceiling);
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t* attr,int prioceiling);

prioceiling 属性包含已初始化互斥对象的优先级上限。 prioceiling 参数的值在 SCHED_FIFO 定义的最大优先级范围内。

int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr, int robust);
// 获得robust属性
int pthread_mutexattr_getrobust(pthread_mutexattr_t *attr, int *robust);
// 恢复锁的状态
int pthread_mutex_consistent(pthread_mutex_t *mutex);

 robust的取值:

  •  PTHREAD_MUTEX_STALLED --> 当一个进程在拥有锁的时候异常挂掉,这时另一个进程企图获得锁将一直阻塞,出现死锁(deadlock)的情况.
  • PTHREAD_MUTEX_ROBUST  --> 这时企图获得锁的进程pthread_mutex_lock将返回EOWNERDEAD而不是0,它提示占用此锁的进程已经挂掉,需要恢复锁,所以如果使用了mutex的robust属性且设置为PTHREAD_MUTEX_ROBUST,在获得锁时要处理pthread_mutex_lock返回的三种情况(不是PTHREAD_MUTEX_ROBUST属性只需处理两种,0成功,其它失败),三种情况是:
    • A:调用成功,锁不用恢复(返回0)
    • B:调用成功,锁需要恢复(返回EOWNERDEAD)
    • C:调用失败(返回其它)

    对于返回情况B,首先应该调用pthread_mutex_consistent函数先恢复锁,再解锁,最后获得锁执行随后的代码.如果未调用pthread_mutex_consistent函数恢复过锁,而在B情况后直接解锁,之后获得锁,那么pthread_mutex_lock将返回ENOTRECOVERABLE,提示此锁已经不能再继续使用,这时就只能pthread_mutex_destroy锁之后重新初始化锁再使用.

1.2.4 mutex属性

int pthread_mutex_setprioceiling (pthread_mutex_t* mutex, int prioceiling, int* old_ceiling)
int pthread_mutex_getprioceiling (const pthread_mutex_t* mutex, int* prioceiling)

set/get优先级上限(这个不是很懂,似乎嵌入式会考虑这个)

int pthread_mutex_consistent (pthread_mutex_t* mutex);

搭配robust属性使用。

1.3 读写锁rwlock

// 创建销毁
int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr);
int pthread_rwlock_destroy (pthread_rwlock_t* rwlock);

// 加读锁
int pthread_rwlock_rdlock (pthread_rwlock_t* rwlock);
int pthread_rwlock_tryrdlock (pthread_rwlock_t* rwlock);
int pthread_rwlock_timedrdlock (pthread_rwlock_t* rwlock, const struct timespec* abstime);
int pthread_rwlock_clockrdlock (pthread_rwlock_t* rwlock, clockid_t clockid, const struct timespec*  abstime);

// 加写锁
int pthread_rwlock_wrlock (pthread_rwlock_t* rwlock);
int pthread_rwlock_trywrlock (pthread_rwlock_t* rwlock);
int pthread_rwlock_timedwrlock (pthread_rwlock_t* rwlock, const struct timespec* abstime);
int pthread_rwlock_clockwrlock (pthread_rwlock_t* rwlock, clockid_t clockid,       const struct timespec* abstime);

// 解锁
int pthread_rwlock_unlock (pthread_rwlock_t* rwlock);

// attr设置
int pthread_rwlockattr_init (pthread_rwlockattr_t* attr);
int pthread_rwlockattr_destroy (pthread_rwlockattr_t* attr);
int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t* attr, int* pshared);
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t* attr, int pshared);
int pthread_rwlockattr_getkind_np (const pthread_rwlockattr_t* attr, int* pref);
int pthread_rwlockattr_setkind_np (const pthread_rwlockattr_t* attr, int* pref);

参数 pref 指定了要设置的读写锁类型。具体取值可以是以下之一:

  • PTHREAD_RWLOCK_PREFER_READER_NP:表示优先考虑读取器。如果设置为此类型,当存在读取器和写入器竞争时,系统会优先分配读取器。这样可以最大程度地保证读取操作的并发性能。
  • PTHREAD_RWLOCK_PREFER_WRITER_NP:表示优先考虑写入器。如果设置为此类型,当存在读取器和写入器竞争时,系统会优先分配写入器。这样可以最大程度地保证写入操作的公平性。

1.4 条件变量cond

// 创建销毁
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
int pthread_cond_init (pthread_cond_t* cond, const pthread_condattr_t* cond_attr);
int pthread_cond_destroy (pthread_cond_t* cond);

// 唤醒
int pthread_cond_signal (pthread_cond_t* cond);
int pthread_cond_broadcast (pthread_cond_t* cond);

// 等待
int pthread_cond_wait (pthread_cond_t* cond, pthread_mutex_t* mutex);
int pthread_cond_timedwait (pthread_cond_t* cond, pthread_mutex_t* mutex,
const struct timespec* abstime);
int pthread_cond_clockwait (pthread_cond_t* cond, pthread_mutex_t* mutex, clockid_t clock_id, const struct timespec* abstime);

// attr设置
int pthread_condattr_init (pthread_condattr_t* attr);
int pthread_condattr_destroy (pthread_condattr_t* attr);
int pthread_condattr_getpshared (const pthread_condattr_t* attr, int* pshared);
int pthread_condattr_setpshared (pthread_condattr_t* attr, int pshared);
// 设置等待时间
int pthread_condattr_getclock (const pthread_condattr_t* attr, clockid_t clock_id);
int pthread_condattr_setclock (pthread_condattr_t* attr, clockid_t clock_id);

1.5 自选锁spinlock

int pthread_spin_init (pthread_spinlock_t* lock, int pshared);
int pthread_spin_destroy (pthread_spinlock_t* lock);
int pthread_spin_lock (pthread_spinlock_t* lock);
int pthread_spin_trylock (pthread_spinlock_t* lock);
int pthread_spin_unlock (pthread_spinlock_t* lock);

1.6 barrier

int pthread_barrier_init (pthread_barrier_t* barrier, const pthread_barrierattr_t * attr, unsigned int count);
int pthread_barrier_destroy (pthread_barrier_t* barrier);
int pthread_barrier_wait (pthread_barrier_t* barrier);
int pthread_barrierattr_init (pthread_barrierattr_t* attr);
int pthread_barrierattr_destroy (pthread_barrierattr_t* attr);
int pthread_barrierattr_getpshared (const pthread_barrierattr_t* attr, int* pshared);
int pthread_barrierattr_setpshared (pthread_barrierattr_t* attr, int pshared);

1.7 线程私有变量

int pthread_key_create (pthread_key_t* key, void (*destr_function) (void *));
// 第二个参数是清理函数
int pthread_key_delete (pthread_key_t __key);
void *pthread_getspecific (pthread_key_t key);
int pthread_setspecific (pthread_key_t key, const void* pointer);
// 设置和获得指定线程的key

二、stl中的函数

2.1 thread库

2.1.1 std::thread

thread(Fn&& fn, Args&&... args)

// 默认构造函数   
thread() noexcept; 
// 接受函数(可以是lambda表达式)及其传递参数的构造函数
template <class _Fn, class... _Args, ...> 
explicit thread(_Fn&& _Fx, _Args&&... _Ax)

// move构造函数
thread(thread&& _Other) noexcept;
// 拷贝构造函数
thread(const thread&) = delete;
// 拷贝赋值运算符
thread& operator=(const thread&) = delete;

示使用例

int main(){
    int arg = 0;
    std::thread t1;                        // t1 is not represent a thread
    std::thread t2(func1, arg + 1);     // pass to thread by value
    std::thread t3(func2, std::ref(arg));  // pass to thread by reference
    std::thread t4(std::move(t3));         // t4 is now running func2(). t3 is no longer a thread
    //t1.join()  Error!
    t2.join();
    //t3.join()  Error!
    t4.join();
}

相关函数

函数名功能描述
id get_id()返回线程的唯一标识符。
bool joinable() 检查线程是否可 join,即是否还在运行。
void join() 等待线程结束执行。
void detach() 将线程与 std::thread 对象分离,使其在后台独立运行。
void swap(std::thread& other) 与另一个 std::thread 对象交换线程。
thread::native_handle_type native_handle()返回线程的原生句柄,用于操作系统特定的线程操作。
bool operator==(const thread& other) const比较两个线程是否相同。
bool operator!=(const thread& other) const比较两个线程是否不同。

2.1.2 std::this_thread

std::this_thread::get_id()  // 获取当前线程id
std::this_thread::sleep_for(/* time */)    //当前线程睡眠
std::this_thread::sleep_until(/*time*/)
std::this_thread::yield()

2.2 mutex库

2.2.1 std::Mutex

// 构造函数
constexpr mutex() noexcept;	
mutex (const mutex&) = delete;
//析构函数
~mutex();

// 成员函数
void lock();
native_handle_type native_handle();
bool try_lock();
void unlock();

2.2.2 recursive_mutex

// 构造函数
constexpr recursive_mutex() noexcept;
recursive_mutex (const recursive_mutex&) = delete;
// 析构函数
~recursive_mutex();

// 成员函数
void lock();
native_handle_type native_handle();
bool try_lock() noexcept;
void unlock();

2.2.3 recursive_timed_mutex

// 构造函数
constexpr recursive_timed_mutex() noexcept;
recursive_timed_mutex (const timed_mutex&) = delete;
// 析构函数
~recursive_timed_mutex();
//If the object is locked on destruction, it causes undefined behavior.

//成员函数
void lock();
native_handle_type native_handle();
bool try_lock() noexcept;
void unlock();

template <class Rep, class Period> 
bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);

template <class Clock, class Duration>
bool try_lock_until (const chrono::time_point<Clock,Duration>& abs_time);

2.2.4 timed_mutex

// 构造函数
constexpr timed_mutex() noexcept;
timed_mutex (const timed_mutex&) = delete;
//析构函数
~timed_mutex();
// If the object is locked on destruction, it causes undefined behavior.

//成员函数
void lock();
native_handle_type native_handle();
bool try_lock();
void unlock();

template <class Rep, class Period>  
bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);

template <class Clock, class Duration>  
bool try_lock_until (const chrono::time_point<Clock,Duration>& abs_time);

2.2.5 unique_lock

构造时加锁。析构时根据锁的状态判断是否可以解锁。

// 构造函数
unique_lock() noexcept;
explicit unique_lock (mutex_type& m);
unique_lock (mutex_type& m, try_to_lock_t tag);
unique_lock (mutex_type& m, defer_lock_t tag) noexcept;
unique_lock (mutex_type& m, adopt_lock_t tag);

template <class Rep, class Period>
unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);


template <class Clock, class Duration>
unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);

unique_lock (const unique_lock&) = delete;
unique_lock (unique_lock&& x);
// 析构函数
~unique_lock();
// 成员函数
void lock();
mutex_type* mutex() const noexcept;
explicit operator bool() const noexcept;

unique_lock& operator= (unique_lock&& x) noexcept;   // C++14中没有noexcept
unique_lock& operator= (const unique_lock&) = delete;

bool owns_lock() const noexcept;
mutex_type* release() noexcept;
void swap (unique_lock& x) noexcept;
bool try_lock();

template <class Rep, class Period>
bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);

template <class Clock, class Duration>  
bool try_lock_until (const chrono::time_point<Clock,Duration>& abs_time);

void unlock();

// 非成员函数重载
template <class Mutex>
void swap (unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;

unique_lock和lock_guard差不多,可以在构建的时候加锁,析构的时候解锁,但是不一样的是,unique_ptr可以在构建的时候加锁或解锁,析构的时候,会根据锁的状态判断是否解锁(即不会重复解锁)。同时中途也可以加锁解锁以及其他的方法。用起来比lock_guard灵活点。

//Passing adopt_lock to the constructor of unique_lock or lock_guard, makes the
//object to not lock the mutex object, and assume instead that it is already locked
//by the current thread.
struct adopt_lock_t {};


//Passing defer_lock to unique_lock's constructor, makes it not to lock the mutex
//object automatically on construction, initializing the object as not owning a
//lock.
struct defer_lock_t {};


//Passing try_to_lock to unique_lock's constructor, makes it to attempt to lock the
//mutex object by calling its try_lock member, instead of lock.
struct try_to_lock_t {};

stl的萃取实现,在stl标准库中,像这种空struct用来偏特化的匹配有很多。

2.2.6 lock_guard

// 构造函数
explicit lock_guard (mutex_type& m);
lock_guard (mutex_type& m, adopt_lock_t tag);
lock_guard (const lock_guard&) = delete;

//Constructs a lock_guard object that keeps m locked.
(1) locking initialization
    The object manages m, and locks it (by calling m.lock()).
(2) adopting initialization
    The object manages m, which is a mutex object currently locked by the constructing thread.



//析构函数
~lock_guard();
/*
Destroys the lock_guard object.
Before that, the destructor calls the unlock member of the mutex object it manages.
*/

2.2.7 call_once

template <class Fn, class... Args>
void call_once (once_flag& flag, Fn&& fn, Args&&... args);
  • flag
struct once_flag {
  constexpr once_flag() noexcept;
  once_flag (const once_flag&) = delete;
  once_flag& operator= (const once_flag&) = delete;
};

用来指明同一flag的call是否已经执行过。

  • fn:函数指针
  • argts:参数
// call_once example
#include <iostream>       // std::cout
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::milliseconds
#include <mutex>          // std::call_once, std::once_flag

int winner;
void set_winner (int x) { winner = x; }
std::once_flag winner_flag;

void wait_1000ms (int id) {
  // count to 1000, waiting 1ms between increments:
  for (int i=0; i<1000; ++i)
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  // claim to be the winner (only the first such call is executed):
  std::call_once (winner_flag,set_winner,id);
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(wait_1000ms,i+1);

  std::cout << "waiting for the first among 10 threads to count 1000 ms...\n";

  for (auto& th : threads) th.join();
  std::cout << "winner thread: " << winner << '\n';

  return 0;
}

/* 输出结果
waiting for the first among 10 threads to count 1000 ms...
winner thread: 2
*/

2.2.8 lock

template <class Mutex1, class Mutex2, class... Mutexes>
void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

例如

std::mutex foo,bar;
std::lock (foo,bar);

这里lockj函数不能保证原子性锁住两个锁,而是按照参数顺序上锁,依然会出现死锁现象。

2.2.9 try_lock

template <class Mutex1, class Mutex2, class... Mutexes> 
int try_lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

返回值:如果所有上所成功,返回-1;否则返回第一个失败的锁的索引。(就是0表示a失败,1表示b失败,2表示c失败,等等)

2.3 condition_variable

2.3.1 condition_variable

// 构造函数
condition_variable();
condition_variable (const condition_variable&) = delete;
// 析构函数
~condition_variable();

//成员函数
void notify_all() noexcept;
void notify_one() noexcept;
void wait (unique_lock<mutex>& lck);

template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);

template <class Rep, class Period>
cv_status wait_for (unique_lock<mutex>& lck,const chrono::duration<Rep,Period>& rel_time);

template <class Rep, class Period, class Predicate>
bool wait_for (unique_lock<mutex>& lck, const chrono::duration<Rep,Period>& rel_time, Predicate pred);

template <class Clock, class Duration>
cv_status wait_until (unique_lock<mutex>& lck, const chrono::time_point<Clock,Duration>& abs_time);

template <class Clock, class Duration, class Predicate>
bool wait_until (unique_lock<mutex>& lck, const chrono::time_point<Clock,Duration>& abs_time, Predicate pred);
  • notify_all:唤醒所有等在在这个cond的线程,没有就什么都不做。
  • notify_one:唤醒一个
  • wait

有两种重载形式,pred是一个函数函数对象,并且是一个不能有输入,返回是bool的调用对象。不太能理解这个如何用,示例如下

// condition_variable::wait (with predicate)
#include <iostream>           // std::cout
#include <thread>             // std::thread, std::this_thread::yield
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable cv;

int cargo = 0;
bool shipment_available() {return cargo!=0;}

void consume (int n) {
  for (int i=0; i<n; ++i) {
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait(lck,shipment_available);
    // consume:
    std::cout << cargo << '\n';
    cargo=0;
  }
}

int main ()
{
  std::thread consumer_thread (consume,10);

  // produce 10 items when needed:
  for (int i=0; i<10; ++i) {
    while (shipment_available()) std::this_thread::yield();
    std::unique_lock<std::mutex> lck(mtx);
    cargo = i+1;
    cv.notify_one();
  }

  consumer_thread.join();

  return 0;
}
  • wait_for:两个重载形式
    • 第一个return cv_status,取值可能为cv_status::timeout或cv_status::no_timeout。
    • 第二个return bool形式,就是returnpred函数的结果。

例如

// condition_variable::wait_for example
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <chrono>             // std::chrono::seconds
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status

std::condition_variable cv;

int value;

void read_value() {
  std::cin >> value;
  cv.notify_one();
}

int main ()
{
  std::cout << "Please, enter an integer (I'll be printing dots): \n";
  std::thread th (read_value);

  std::mutex mtx;
  std::unique_lock<std::mutex> lck(mtx);
  while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
    std::cout << '.' << std::endl;
  }
  std::cout << "You entered: " << value << '\n';

  th.join();

  return 0;
}
  • wait_until:输入输出同理。

2.3.2 condition_variable_any

区别于前面的condition_variable,前面的condition_variable只能采用unique_lock作为输入,而这个condition_variable_any可以直接使用std::mutex

// 构造函数
condition_variable_any();
condition_variable_any (const condition_variable_any&) = delete;
// 析构函数
~condition_variable_any();


// 成员函数
void notify_all() noexcept;
void notify_one() noexcept;

template <class Lock>
void wait (Lock& lck);
	
template <class Lock, class Predicate>
void wait (Lock& lck, Predicate pred);

template <class Lock, class Rep, class Period>
cv_status wait_for (Lock& lck, const chrono::duration<Rep,Period>& rel_time);

template <class Lock, class Rep, class Period, class Predicate>
bool wait_for (Lock& lck, const chrono::duration<Rep,Period>& rel_time, Predicate pred);

template <class Lock, class Clock, class Duration>
cv_status wait_until (Lock& lck, const chrono::time_point<Clock,Duration>& abs_time);

template <class Lock, class Clock, class Duration, class Predicate>
bool wait_until (Lock& lck, const chrono::time_point<Clock,Duration>& abs_time, redicate pred);

用法和前面的一样。

2.4 atomic库

  stl的原子操作库。在看原子库的时候,建议先看一下这片的内存order:聊聊 内存模型与内存序

2.4.1 atomic

template <class T>
struct atomic;

atomic保证了多线程对原子变量访问无竞争态,但是,这可能是通过锁或者原子指令实现的,如果模板参数是基本整型以及cstdint中的扩展类型,则会是其偏特化版本,其原子性是通过原子指令,并且具有额外的成员变量,除了bool类型。

构造函数
atomic() noexcept = default;
constexpr atomic (T val) noexcept;
atomic (const atomic&) = delete;
operator=
T operator= (T val) noexcept;
T operator= (T val) volatile noexcept;

atomic& operator= (const atomic&) = delete;
atomic& operator= (const atomic&) volatile = delete;
通用成员变量
is_lick_free
// is_lock_free:判断原子类型是否通过加锁实现
bool is_lock_free() const volatile noexcept;
bool is_lock_free() const noexcept;

并不是说只有基本类型会用锁,例如

#include <iostream>
#include <utility>
#include <atomic>
 
struct A { int a[100]; };
struct B { int x, y; };
int main(){
    std::cout << std::boolalpha
              << "std::atomic<A> is lock free? "
              << std::atomic<A>{}.is_lock_free() << '\n'
              << "std::atomic<B> is lock free? "
              << std::atomic<B>{}.is_lock_free() << '\n';
}

/* 可能输出
std::atomic<A> is lock free? false
std::atomic<B> is lock free? true
*/
store
void store (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
void store (T val, memory_order sync = memory_order_seq_cst) noexcept;
load
T load (memory_order sync = memory_order_seq_cst) const volatile noexcept;
T load (memory_order sync = memory_order_seq_cst) const noexcept;
operator T

原子的赋值

std::atomic<int> foo;
foo = x;
exchange
T exchange (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T exchange (T val, memory_order sync = memory_order_seq_cst) noexcept;

原子的修改。

compare_exchange_weak和compare_exchange_strong
bool compare_exchange_weak (T& expected, T val, memory_order sync=memory_order_seq_cst) volatile noexcept;
bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) noexcept;
bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) noexcept;


bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst) noexcept;
bool compare_exchange_strong (T& expected, T val, memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_strong (T& expected, T val, memory_order success, memory_order failure) noexcept;

两个函数都是原子操作,但是weak版本可能会出现实际值与预期值相等,但是返回false的情况,而strong则不会,性能上自然weak更快点。

特定类型的成员函数
T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_add (T val, memory_order sync = memory_order_seq_cst) noexcept;
T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;

T fetch_and (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_and (T val, memory_order sync = memory_order_seq_cst) noexcept;

T fetch_or (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_or (T val, memory_order sync = memory_order_seq_cst) noexcept;

T fetch_sub (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_sub (T val, memory_order sync = memory_order_seq_cst) noexcept;
T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;

T fetch_xor (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_xor (T val, memory_order sync = memory_order_seq_cst) noexcept;

T operator--() volatile noexcept;
T operator--() noexcept;
T operator-- (int) volatile noexcept;
T operator-- (int) noexcept;
T operator+= (T val) volatile noexcept;
T operator+= (T val) noexcept;
T operator-= (T val) volatile noexcept;
T operator-= (T val) noexcept;
T operator&= (T val) volatile noexcept;
T operator&= (T val) noexcept;
T operator|= (T val) volatile noexcept;
T operator|= (T val) noexcept;
T operator^= (T val) volatile noexcept;
T operator^= (T val) noexcept;
T operator+= (ptrdiff_t val) volatile noexcept;
T operator+= (ptrdiff_t val) noexcept;
T operator-= (ptrdiff_t val) volatile noexcept;
T operator-= (ptrdiff_t val) noexcept;
T operator++() volatile noexcept;
T operator++() noexcept;
T operator++ (int) volatile noexcept;
T operator++ (int) noexcept;

2.4.2 atomic_flag

布尔原子变量。

// 构造函数
atomic_flag() noexcept = default;
atomic_flag (const atomic_flag&T) = delete;

// 成员函数
bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept;
bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept;

void clear (memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear (memory_order sync = memory_order_seq_cst) noexcept;

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值