Linux Pthread相关学习记录,自旋锁、读写锁、线程池有待完善

文章目录

  • 线程基础
  • 线程调度竞争范围
  • 线程模型
    • N:1用户线程模型
    • 1:1核心线程模型
    • N:M混合线程模型
  • 线程创建
    • 错误检查
    • 线程属性
      • 初始化与销毁属性
      • 获取与设置分离属性
      • 获取和设置栈大小
      • 获取与设置栈移出保护区大小
      • 获取与设置线程竞争范围
      • 获取与设置调度策略
      • 获取与设置继承的调度策略
      • 获取与设置调度参数
  • 并发级别
  • 退出线程
  • join线程/detach线程
  • 返回当前线程id
  • 取消线程执行
  • 线程特定数据
  • 示例
  • POSIX信号量
    • sem_open
    • sem_close
    • sem_unlink
    • sem_init
    • sem_destory
    • sem_wait
    • sem_post
  • 同步
    • 互斥
      • pthread_mutex_init
      • pthread_mutex_lock
      • pthread_mutex_unlock
      • pthread_mutex_destory
    • join
    • 自旋锁
      • pthread_spin_init
      • pthread_spin_destory
      • pthread_spin_lock
      • pthread_spin_unlock
    • 读写锁
      • pthread_rwlock_init
      • pthread_rwlock_destory
      • pthread_rwlock_rdlock
      • pthread_rwlock_wrlock
      • pthread_rwlock_unlock
    • 条件变量
      • pthread_cond_init
      • pthread_cond_destroy
      • pthread_cond_wait
      • pthread_cond_signal、pthread_cond_broadcast
      • 条件变量使用规范
  • 线程池

线程基础

  • 线程操作包括线程创建、终结、同步(joins,blocking)、调度、数据管理以及进程交互
  • 一个线程不维护创建线程列表,不知道其创建线程
  • 在同一进程的线程共享:
    • 进程指令
    • 大部分数据
    • 文件描述符
    • 信号及信号处理函数
    • 当前工作目录
    • 用户和组id
  • 不同线程拥有唯一的:
    • 线程id
    • 一系列寄存器,堆栈指针
    • 维护局部变量及返回地址的栈
    • 信号掩码(signal mask)
    • 优先级
    • 全局返回值:errno
  • pthread函数成功返回值为0

线程调度竞争范围

  • 操作系统提供了各种模型,用来调度应用程序创建的线程。这些模型之间的主要不同是:在竞争系统资源(特别是CPU时间)时,线程调度竞争范围不一样
  • 进程竞争范围:各个线程在同一进程竞争“被调度的CPU时间”(但不直接和其他进程中的线程竞争)
  • 系统竞争范围:线程直接和“系统范围”内的其他线程竞争

线程模型

N:1用户线程模型

  • 线程实现建立在进程控制机制至上,由用户空间的程序库来管理。OS内核完全不知道线程信息。这些线程称为用户空间线程。
  • 这些线程都工作在“进程竞争范围
  • 在N:1线程模型中,内核不干涉线程的任何生命活动,也不干涉同一进程中的线程环境切换
  • 在N:1线程模型中,一个进程中的多个线程只能调度到一个CPU,这种约束限制了可用的并行总量
  • 第二个缺点是如果某个线程执行了一个“阻塞式”操作(如read),那么进程中的所有线程都会被阻塞,直到那个操作结束。为此,一些线程的实现是为这些阻塞式函数提供包装器,用非阻塞版本替换这些系统吊桶,用以消除这种限制。

1:1核心线程模型

  • 1:1核心线程模型中,应用程序创建的每一个线程都由一个核心线程直接管理
  • OS内核将每一个核心线程都调到系统CPU上,因此,所有线程都工作在“系统竞争范围”
  • 这种线程的创建与调度由内核完成,因为这种线程的系统开销比较大(比进程开销小)

N:M混合线程模型

  • N:M混合线程模型提供了两级控制,将用户线程映射为系统的可调度体以实现并行,这个可调度体称为轻量级进程(LWP:light weight process),LWP再一一映射到核心线程。

线程创建

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);返回值
成功:返回0
失败:返回错误码,strerror(ret)返回错误描述
参数 说明
thread 线程id
attr 线程属性
start_routine 函数指 针
arg 函数的参数,可以只用指向结构体的指针传入一系列参数

错误检查

  • 传统一些函数,成功返回0,失败返回-1,并且设置errno指示错误
  • pthreads函数出错时不会设置全局变量errno(大部分POSIX函数会这么做)。而是将错误代码通过返回值返回
  • pthreads同样也提供了线程内的errno变量,以支持其他使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量开销小

线程属性

初始化与销毁属性

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destory(pthread_attr_t *attr);

获取与设置分离属性

#include <pthread.h>int pthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate);int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);detachstate:PTHREAD_CREATE_DETACHED/PTHREAD_CREATE_JOINABLE(默认)

获取和设置栈大小

#include <pthread.h>int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,size_t *restrict stacksize);int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

获取与设置栈移出保护区大小

#include <pthread.h>
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);

获取与设置线程竞争范围

#include <pthread.h>int pthread_attr_getscope(const pthread_attr_t *restrict attr,int *restrict contentionscope);int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
contentionscope值 说明
PTHREAD_SCOPE_SYSTEM indicating a scheduling contention scope that is system-wide
PTHREAD_SCOPE_PROCESS which indicates a process scheduling contention scope.

获取与设置调度策略

#include <pthread.h>int pthread_attr_getschedpolicy(const pthread_attr_t *restrict attr,int *restrict policy);int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
policy 说明
SCHED_FIFO 先进先出
SCHED_RR 抢占
SCHED_OTHER 默认

获取与设置继承的调度策略

#include <pthread.h>int pthread_attr_getinheritsched(const pthread_attr_t *restrict attr,int *restrict inheritsched);int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
inheritsched 说明
PTHREAD_INHERIT_SCHED Indicates that the newly created thread should inherit all it’s scheduling related attributes from it’s creating thread. It ignores the values of the relevant attributes within the attr argument.(默认值)
PTHREAD_EXPLICIT_SCHED Indicates that the newly created thread should set it’s scheduling related attributes based on attr argument.

获取与设置调度参数

#include <pthread.h>int pthread_attr_getschedparam(const pthread_attr_t *restrict attr,struct sched_param *restrict param);int pthread_attr_setschedparam(pthread_attr_t *restrict attr,const struct sched_param *restrict param);struct sched_param { int sched_priority;  char __opaque[__SCHED_PARAM_SIZE__]; };sched_priority:默认值为0

并发级别

#include <pthread.h>int pthread_setconcurrency(int new_level);
int pthread_getconcurrency(void);返回值:
pthread_setconcurrency
成功:0
失败:错误码pthread_getconcurrency
返回level

仅在N:M线程模型中有效,设置并发级别,给内核一个提示:表示提供给定级别数量的核心线程来映射用户线程是高效的。

退出线程

void pthread_exit(void *retval);retval:线程返回值

该函数方法用于结束线程并且没有返回值。如果线程未分离,则可以使用pthread_join从另一个线程检查线程id和返回值

join线程/detach线程

#include <pthread.h>int pthread_join(pthread_t thread, void **value_ptr);介绍
当前线程等待目标线程运行结束pthread_join成功返回非NULL value_ptr值,传递给终结线程
pthread_exit函数的值存储在value_ptr值中。当pthread_join成功返回,目标线程终结.对同一目标线程同时调用多个pthread_join函数则未定义。
如果pthread_join被取消,目标线程不会被分离.
#include <pthread.h>int pthread_detach(pthread_t thread);

在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

线程通过调用pthread_join函数等待其他线程终止。pthread_join函数会阻塞,直到线程tid终止,将线程例程返回的(void*)指针赋值为thread_return指向的位置,然后回收已终止线程占用的所有存储器资源。

pthread_detach用于分离可结合线程tid。线程能够通过以pthread_self()为参数的pthread_detach调用来分离它们自己。
如果一个可结合线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

返回当前线程id

#include <pthread.h>pthread_t pthread_self(void);pthread_self返回调用该函数的线程ID

取消线程执行

#include <pthread.h>int pthread_cancel(pthread_t thread);介绍
该函数使目标线程停止执行。由其他线程执行。
The tar-get thread's cancelability state and type determines when the
cancellation takes effect.  When the cancellation is acted on, the
cancellation cleanup handlers for thread are called.  When the last
cancellation cleanup handler returns, the thread-specific data destructor
functions will be called for thread.  When the last destructor function returns,
thread will be terminated.The cancellation processing in the target thread runs asynchronously with
respect to the calling thread returning from pthread_cancel().A status of PTHREAD_CANCELED is made available to any threads joining
with the target.  The symbolic constant PTHREAD_CANCELED expands to a
constant expression of type (void *), whose value matches no pointer to
an object in memory nor the value NULL.返回值
成功:0
失败:返回错误码

线程特定数据

  • 在单线程程序中,经常用“全局变量”实现多个函数之间共享数据
  • 多线程环境下,数据空间是共享的,因此全局变量也要为所有线程所共享
  • 在应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但是可以跨多个函数访问
  • POSIX线城库通过维护一定的数据结构来解决该问题,该种数据结构称为(TSD:Thread specific Data)


key-value形式

下面说一下线程存储的具体用法。

  1. 创建一个类型为pthread_key_t类型的变量。
  2. 调用pthread_key_create()来创建该变量。该函数有两个参数,第一个参数就是上面声明的pthread_key_t变量,第二个参数是一个清理函数,用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL,这样系统将调用默认的清理函数。该函数成功返回0.其他任何返回值都表示出现了错误。
  3. 当线程中需要存储特殊值的时候,可以调用 pthread_setspcific() 。该函数有两个参数,第一个为前面声明的pthread_key_t变量,第二个为void*变量,这样你可以存储任何类型的值。
  4. 如果需要取出所存储的值,调用pthread_getspecific()。该函数的参数为前面提到的pthread_key_t变量,该函数返回void *类型的值。下面是前面提到的函数的原型:
#include <pthread.h>int pthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_key_delete(pthread_key_t key);int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
在线程函数中设置,不管多少线程,只执行一次
本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。

示例

#include <iostream>
#include <pthread.h>using namespace std;void * print_message_function(void *ptr);int main() {pthread_t thread1,thread2;char* message1 = "thread 1";char* message2 = "thread 2";int ret1,ret2;ret1 = pthread_create(&thread1,NULL,print_message_function,(void*)message1);ret2 = pthread_create(&thread2,NULL,print_message_function,(void*)message2);pthread_join(thread1,NULL);pthread_join(thread2,NULL);cout<<"thread 1 return :"<<ret1<<endl;cout<<"thread 2 return :"<<ret2<<endl;return 0;
}void * print_message_function(void* ptr){char *message;message = (char*)ptr;if(message){cout<<message<<endl;}
}

POSIX信号量

sem_open

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);返回值
成功:信号量指针
失败:SEM_FAILED

使用一个现有的有名信号量时,指定连个参数name、oflag参数值为0。当oflag值为O_CREAT标记集时,有名信号量不存在时,创建新的信号量,如果它已经存在,则使用。

指定O_CREAT时需要提供额外的参数,mode表示打开文件的额权限。当打开一个现有的信号量时接口不允许指定模式。value表示信号量的值取值为0——SEM_VALUE_MAX。

oflag=O_CREAT|O_EXCL时,信号量已经存在,导致sem_open返回失败

sem_close

#include <semaphore.h>
int sem_close(sem_t *sem);介绍
如果进程没有首先调用sem_close退出,内核将自动关闭任何打开的信号量。该操作不会影响信号量的状态。返回值
成功:0
失败:-1

sem_unlink

#include <semaphore.h>int sem_unlink(const char *name);介绍
删除信号量的名字,如果没有打开的信号量引用,该信号量被销毁,否则销毁将延迟到最后一个打开的引用关闭。
返回值
成功:0
失败:-1

sem_init

#include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);介绍
sem_init() 初始化一个定位在 sem 的匿名信号量。
value 参数指定信号量的初始值。返回值
sem_init() 成功时返回 0;错误时,返回 -1,并把 errno 设置为合适的值。
pshared 说明
0 那么信号量将被进程内的线程共享,并且应该放置在这个进程的所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。
非零值 信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。因为通过 fork(2) 创建的孩子继承其父亲的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都可以用 sem_post(3)、sem_wait(3) 等等操作信号量。初始化一个已经初始的信号量其结果未定义。

sem_destory

#include <semaphore.h>int sem_destroy(sem_t *sem);介绍
Only a semaphore that has been initialized  by  sem_init(3)  should  be
destroyed using sem_destroy().返回值
成功:0
失败:-1,errrno设置对应错误值

sem_wait

#include <semaphore.h>int sem_wait(sem_t *sem);介绍
sem_wait()  decrements (locks) the semaphore pointed to by sem.  If the
semaphore's value is greater than zero, then  the  decrement  proceeds,
and  the function returns, immediately.  If the semaphore currently has
the value zero, then the call blocks until either it  becomes  possible
to  perform the decrement (i.e., the semaphore value rises above zero),
or a signal handler interrupts the call.返回值
成功:0
失败:-1,errrno设置对应错误值

sem_post

#include <semaphore.h>int sem_post(sem_t *sem);介绍
sem_post()  increments  (unlocks)  the semaphore pointed to by sem.  If
the semaphore's value consequently  becomes  greater  than  zero,  then
another  process  or thread blocked in a sem_wait(3) call will be woken
up and proceed to lock the semaphore.返回值
成功:0
失败:-1,errrno设置对应错误值

同步

三种线程同步方法

  • 互斥锁:阻止其他线程访问变量。这强制线程对变量或变量集进行独占访问。
  • 连接:使线程等待其他人完成(终止)。
  • 条件变量:数据类型pthread_cond_t

互斥

互斥锁用于防止因竞争条件导致的数据不一致。当两个或多个线程需要在同一存储区域上执行操作时经常发生竞争条件,但计算结果取决于执行这些操作的顺序。互斥锁用于序列化共享资源。每当多个线程访问全局资源时,资源应该具有与之关联的互斥锁。可以应用互斥锁来保护一段内存(“关键区域”)与其他线程。互斥体只能应用于单个进程中的线程,并且不像信号量那样在进程之间工作。

pthread_mutex_init

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric attr);
pthread_mutexattr_t 说明
PTHREAD_MUTEX_TIMED_N 这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
PTHREAD_MUTEX_RECURSIVE_NP 嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
PTHREAD_MUTEX_ERRORCHECK_NP 检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
PTHREAD_MUTEX_ADAPTIVE_NP 适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

pthread_mutex_lock

#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);介绍
给互斥量加锁,如果该互斥量已经加锁,那么该线程将等待互斥量解锁。返回值
成功:0
失败:错误码

pthread_mutex_unlock

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);返回值
成功:0
失败:错误码

pthread_mutex_destory

#include <pthread.h>
int pthread_mutex_destory(pthread_mutex_t *mutex);返回值
成功:0
失败:错误码

没有互斥锁

int counter=0;/* Function C */
void functionC()
{counter++}

有互斥锁

/* Note scope of variable and mutex are the same */
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int counter=0;/* Function C */
void functionC()
{pthread_mutex_lock( &mutex1 );counter++pthread_mutex_unlock( &mutex1 );
}

可能的执行顺序

无互斥锁

thread 1 thread 2
counter=0 counter=0
counter=1 counter=1

有互斥锁

thread 1 thread 2
counter=0 counter=0
counter=1 线程2被锁,线程1排它性访问变量counter
counter=2

如果用于递增变量counter的寄存器加载和存储操作 以不幸的时序发生,理论上可以使每个线程递增并用相同的值覆盖相同的变量。另一种可能性是,线程2首先递增计数器 锁定线程1直到完成,然后线程1将其递增到2。

有互斥锁

thread 1 thread 2
counter=0 counter=0
线程1被锁,线程2排它性访问变量counter counter=1
counter=2
#include <iostream>
#include <pthread.h>
using namespace std;void *func(void* ptr);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int countN = 0;
int main() {pthread_t thread1,thread2;int rc1 ,rc2;if((rc1= pthread_create(&thread1,NULL,&func,NULL))){cout<<"error occurs"<<endl;}if((rc2=pthread_create(&thread2,NULL,&func,NULL))){cout<<"error occurs"<<endl;}pthread_join(thread1,NULL);pthread_join(thread2,NULL);return 0;
}
void* func(void* ptr){pthread_mutex_lock(&mutex1);countN++;cout<<countN<<endl;pthread_mutex_unlock(&mutex1);
}

join

可以使用join等待其他进程结束。线程调用例程可以启动多个线程,然后等待他们完成以获得结果。其中一个线程使用join等待其他线程结束

#include <iostream>
#include <pthread.h>
#include <cstdio>
#define NUM 10
void * func(void* ptr);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int countN=0;
using namespace std;
int main() {pthread_t th[NUM];for(int i = 0;i<NUM;i++){pthread_create(&th[i],NULL,&func,NULL);}for(int i = 0;i<NUM;i++){pthread_join(th[i],NULL);}cout<<countN<<endl;return 0;
}void* func(void* ptr){printf("thread num:%ld\n",pthread_self());pthread_mutex_lock(&mutex1);countN++;pthread_mutex_unlock(&mutex1);
}

自旋锁

自旋锁类似于互斥锁,它的性能比互斥锁更高。
自旋锁与互斥锁的一个重要区别在于,线程在申请自旋锁的时候,线程不会被挂起,处于忙等待的状态

pthread_spin_init

pthread_spin_destory

pthread_spin_lock

pthread_spin_unlock

读写锁

  • 只要没有现成持有给定的读写锁用于写,那么任意数目的线程可以持有读写锁用于读
  • 仅当没有线程持有某个给定的读写锁用于读或用于写时,才能分配读写锁用于写
  • 读写锁用于读称为共享锁,读写锁用于写称为排它锁

pthread_rwlock_init

pthread_rwlock_destory

pthread_rwlock_rdlock

pthread_rwlock_wrlock

pthread_rwlock_unlock

条件变量

  • 当一个线程互斥地访问某个变量时,他可能发现在其他线程改变状态之前,什么也不做。例如一个线程访问队列时,返现队列为空,它只能等待,只要其他线程讲一个节点天剑到队列中。这种情况就需要使用条件变量。
  • 条件变量是pthread_cond_t类型的变量。通过使用适当的函数可以等待或者继续执行。条件变量机制允许线程暂停执行并放弃处理器直到某些条件为真。条件变量必须始终与互斥锁相关联,以避免由一个准备等待的线程创建的竞争条件和另一个可能在第一个线程实际等待它之前发出条件的线程,从而导致死锁。线程将永远等待从未发送的信号。可以使用任何互斥锁,互斥锁和条件变量之间没有明确的链接。

pthread_cond_init

#include <pthread.h>int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);介绍
The pthread_cond_init() function creates a new condition variable, with
attributes specified with attr.  If attr is NULL the default attributes
are used.
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;返回值
成功:0
失败:错误码

pthread_cond_destroy

#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond);介绍
The pthread_cond_destroy() function frees the resources allocated by the
condition variable cond.返回值
成功:0
失败:错误码

pthread_cond_wait

条件等待
pthread_cond_wait
pthread_cond_timedwait - place limit on how long it will block.

隐含的操作

  1. 首先对mutex进行解锁
  2. 等待条件,直到有现成向他发起通知
  3. 重新对互斥量进行加锁操作

pthread_cond_signal、pthread_cond_broadcast

基于条件唤醒线程
pthread_cond_signal
pthread_cond_broadcast - wake up all threads blocked by the specified condition variable.

pthread_cond_signal会向第一个等待条件的线程发起通知,如果灭有任何一个线程处理等待条件的状态,这个通知将被忽略

pthread_cond_broadcast向所有等待线程发起通知

条件变量使用规范

等待条件代码使用while原因,pthread_cond_wait可能会产生虚假唤醒

等待条件代码
pthread_mutex_lock(&mutex);
while(条件为假)pthread_cond_wait(cond,mutex);
修改条件
pthread_mutex_unlock(&mutex);给条件发送信号代码
pthread_mutex_lock(&mutex);
设置条件为真
pthread_signal(cond);
pthread_mutex_unlock(&mutex);
#include <iostream>
#include <pthread.h>using namespace std;pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void* func1(void* ptr);
void* func2(void* ptr);int countN = 0;
#define COUNT_DONE 10
#define COUNT_HALT1 3
#define COUNT_HALT2 6int main() {pthread_t thread1,thread2;pthread_create(&thread1,NULL,func1,NULL);pthread_create(&thread2,NULL,func2,NULL);pthread_join(thread1,NULL);pthread_join(thread2,NULL);return 0;
}void *func1(void* ptr){for(;;){pthread_mutex_lock(&cond_mutex);while(countN  >= COUNT_HALT1&&countN<=COUNT_HALT2){printf("in func1 while count is %d\n",countN);pthread_cond_wait(&cond,&cond_mutex);}pthread_mutex_unlock(&cond_mutex);pthread_mutex_lock(&count_mutex);countN++;printf("func1 countN is %d\n",countN);pthread_mutex_unlock(&count_mutex);if(countN>=COUNT_DONE) return NULL;}
}void* func2(void* ptr){for(;;){pthread_mutex_lock(&cond_mutex);if(countN<COUNT_HALT1||countN>COUNT_HALT2){printf("in func2 if count is %d\n",countN);pthread_cond_signal(&cond);}pthread_mutex_unlock(&cond_mutex);pthread_mutex_lock(&count_mutex);countN++;printf("func2 countN is %d\n",countN);pthread_mutex_unlock(&count_mutex);if(countN>=COUNT_DONE) return NULL;}
}

当countN值在COUNT_HALT1和COUNT_HALT2之间时,线程1将暂停。能够确定的事情是func2当countN值在COUNT_HALT1和COUNT_HALT2之间时会增加countN值。其他都是随机值。

必须选择逻辑条件(“if”和“while”语句)以确保在处理“等待”时执行“信号”。糟糕的软件逻辑也可能导致死锁情况。

这个例子中有大量的竞态条件,因为count用作条件,不能在while语句中锁定而不导致死锁。我将使用一个更简洁的例子但它是一个条件变量的例子。

线程池

  • 用于执行大量相对短暂的任务
  • 当任务增加的时候能够动态的增加线程池的数量直到达到一个阈值
  • 当任务执行完毕时,能够动态的销毁线程池中的线程
  • 该线程池的本质是生产者与消费者模型的应用。生产者线程向任务队列中添加任务,一旦队列有任务到来,如果有等待线程就唤醒来执行任务,如果没有等待线程并且线程数量没有达到阈值,就创建新线程来执行任务
任务类型 线程数
计算密集型任务 线程个数=CPU个数
I/O密集型任务 线程个数>CPU个数

Linux Pthread学习记录相关推荐

  1. linux个人学习记录

    linux学习记录 资料: Linux 黑马程序员_bilibili AcWing Linux基础课 可能是东半球最全面易懂的 Tmux 使用教程! Shell 教程 | 菜鸟教程 (runoob.c ...

  2. Linux的学习记录。

    linux基础学习.(第一天 本人用的是centos7 声明:此博客用来记录每一天的学习,会努力坚持的更新下去.希望能跟初学linux的小伙伴们一起分享当天所得. 也请小伙伴们多多指正博客中的错误!! ...

  3. Zabbix3.2下Template App Zabbix Server+Template OS Linux Item学习记录

    就是Zabbix server默认的78个Item学习记录,返回值是在我自己的虚拟机上通过zabbix_get或者zabbix_agentd获取的,zabbix internal check的item ...

  4. Linux+shell学习记录和思维导图

    由于shell和Linux学习分不开,所以干脆一起结合起来学习,顺便用思维导图工具做一个记录. 学习的关键在于对着教程敲代码. 学习工具 思维导图工具Xmind:以前一直用百度脑图做一些简单的记录,但 ...

  5. linux c++ 学习记录

    此文章用以记录所有在使用Linux c++中遇到的问题,方便自己以后查找解决方案. 由于笔者算法的开发和仿真大多都是使用windows 平台, linux 主要是为了测试systemc code,为了 ...

  6. Linux驱动学习记录-6.设备树的LED驱动

    这一章使用第五章的设备树知识来写led驱动 1.修改设备树 在根节点下面添加子节点 alphaled {#address-cells = <1>;#size-cells = <1&g ...

  7. Linux驱动学习记录 cpu主频

    以imx6ull芯片为例 imx6ull芯片主频是792MHz 查看cpu信息 cat /proc/cpuinfo proc/cpuinfo processor : 0 model name : AR ...

  8. Linux命令学习记录(六)

    用户管理命令,以root权限使用 adduser:添加新用户名 passwd:修改密码 deluser:删除用户 群组管理命令,以root权限使用 addgroup:添加群组 usermod:修改用户 ...

  9. linux命令学习记录一

    1.tree -L 1 /        -L 1 (数字1) 表示显示"/"下目录的层次,1表示一层 CentOS6.5下tree命令-bash: tree: command n ...

  10. [奔跑吧 Linux内核][学习记录]编译内核-实验1-2-[环境以及参考]

    1.下载的版本 VMware  workstation player v16.2.4 Ubuntu v22.04.1 Linux kernel v5.19 2.参考的文章 环境安装 [干货]win10 ...

最新文章

  1. Codeforces Round #555 (Div. 3) AB
  2. 前端一HTML:七:css初步认识
  3. iOS开发之AFNetworking 3.0.4使用
  4. [蓝桥杯2018初赛]乘积尾零-数论
  5. qt中实现左右分割线_Qt项目中,实现屏幕截图并生成gif的详细示例(值得细读)...
  6. 树莓派 小屏幕_树莓派学习手动积累(1)
  7. 又烧一辆!蔚来ES8维修时起火烧到只剩底盘 官方回应:已开启调查
  8. python文件操作大全
  9. 2020-08-09
  10. 数据库原理(一)—— 关系代数(二)
  11. JDK 8.0 新特性——函数式接口和Lambda 表达式
  12. 消色差、半复消色差、复消色差的区别
  13. 华为笔试题 字符串解压缩(C语言解法)
  14. 蓝桥杯、PAT、CCF CSP、团体程序设计天梯赛、传智杯、全国高校计算机能力挑战赛、软考等大学生编程比赛/考试介绍
  15. vue当前浏览器是否为ie_vue判断当前浏览器为IE低版本,给出升级提示;IE11及其他浏览器正常使用...
  16. 前端工程师的职业规划
  17. kali使用笔记本自带无线网卡_kali破解wifi握手包-GPU破解,速度快到无法想象
  18. qt c语言混合编程 pdf,QT中的C++技术 pdf
  19. 第三方配件查验苹果MFi认证
  20. 记微博上一次“落荒而逃”

热门文章

  1. beego 最新版本_LFA (Linux For All) Build 191111 发布,基于Ubuntu由最新内核支持的新版本...
  2. Struts2 教程
  3. 计算机公式与函数乘法,excel里减法函数是哪个?-excel函数公式乘法
  4. 怎么录制音频,什么录音软件好用?
  5. 同义词词林 使用 java_利用同义词林计算词的相似度——基于路径与深度的同义词词林词语相似度计算...
  6. python自动化办公——PIL根据模板生成结业证书
  7. 高通 MSM 8916与MSM8926芯片的区别
  8. 什么是即时通讯系统?
  9. 蓝桥杯等差素数列(暴力)
  10. 基于 HttpClient 4.5 的 HttpClientUtils