Linux下多线程编程详解简介

简介

  • 线程创建
  • 线程属性设置
  • 线程参数传递
  • 线程优先级
  • 线程的数据处理
  • 线程的分离状态
  • 互斥锁
  • 信号量

一 线程创建

废话不多说,先上一个线程版的helloworld。

#include <iostream>
using namespace std;

void *run(void *ptr){
        for(int i=0; i<3; i++) {
          sleep(1);
                cout<<"hello world "<<i<<endl;
        }
        return 0;
}
int main(){
        pthread_t id;
        int ret=0;
        ret=pthread_create(&id,NULL,run,NULL);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        return 0;
}

运行后

hello world 0
hello world 1
hello world 2

上面的代码很简单,就是启动一个线程,然后先线程里循环打印字段字符串。我们就以这个最简单的例子来开口。

1.1 pthread_create

创建一个线程,函数的声明:

int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
                   void* (*start_routine)(void*), void* arg)
  • thread_out 创建线程后的标识符,下面会介绍。
  • attr 设置线程属性。传NULL为默认属性(对大多数程序来说,使用默认属性就够了),当然具体使用下面也会做介绍。
  • start_routine 线程运行函数的起始地址(简单说就是函数指针)。
  • arg 运行函数的参数,这里我们没有使用参数,就直接NULL。

创建成功返回0。若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。

1.2 pthread_t

定义在 pthreadtypes.h

typedef unsigned long int pthread_t;

线程的标识符。也就是前面创建线程时候传入的参数。当然函数参数做输入的时候,传的是地址。

二 线程属性设置

同样,我们先上示例代码

#include <iostream>
using namespace std;

void *run(void *ptr){
        int value=*(int *)ptr;
        for(int i=0; i<3; i++) {
                sleep(1);
                cout<<"value  "<<value<<endl;
        }
        return 0;
}
int main(){
        int ret=0;
        int value=10;
        pthread_t id;
        pthread_attr_t attr;
        //初始化
        pthread_attr_init(&attr);
        //设置相关属性
        pthread_attr_setscope (&attr,PTHREAD_SCOPE_PROCESS);
        ret=pthread_create(&id,&attr,run,&value);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        return 0;
}

代码相对于上一节中多了一个属性设置和参数传递。涉及变量pthread_attr_t、pthread_attr_init函数等。整个流程三步走

    1. 定义属性变量pthread_attr_t
    1. 初始化pthread_attr_t
    1. 创建线程时传入。

下面我们具体介绍

2.1 pthread_attr_t

属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。pthread_attr_t结构的定义,定义在pthread.h

typedef struct
{
    uint32_t flags;
    void * stack_base;
    size_t stack_size;
    size_t guard_size;
    int32_t sched_policy;
    int32_t sched_priority;
} pthread_attr_t;
2.2 属性设置
  • 属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。
  • 调用相关属性设置的方法pthread_attr_set×××即可。

下面列举相关属性操作函数,具体方法含义就不做介绍。

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

int pthread_attr_setdetachstate(pthread_attr_t * attr, int state);
int pthread_attr_getdetachstate(pthread_attr_t const * attr, int * state);

int pthread_attr_setschedpolicy(pthread_attr_t * attr, int policy);
int pthread_attr_getschedpolicy(pthread_attr_t const * attr, int * policy);

int pthread_attr_setschedparam(pthread_attr_t * attr, struct sched_param const * param);
int pthread_attr_getschedparam(pthread_attr_t const * attr, struct sched_param * param);

int pthread_attr_setstacksize(pthread_attr_t * attr, size_t stack_size);
int pthread_attr_getstacksize(pthread_attr_t const * attr, size_t * stack_size);

int pthread_attr_setstackaddr(pthread_attr_t * attr, void * stackaddr);
int pthread_attr_getstackaddr(pthread_attr_t const * attr, void ** stackaddr);

int pthread_attr_setstack(pthread_attr_t * attr, void * stackaddr, size_t stack_size);
int pthread_attr_getstack(pthread_attr_t const * attr, void ** stackaddr, size_t * stack_size);

int pthread_attr_setguardsize(pthread_attr_t * attr, size_t guard_size);
int pthread_attr_getguardsize(pthread_attr_t const * attr, size_t * guard_size);

int pthread_attr_setscope(pthread_attr_t *attr, int  scope);
int pthread_attr_getscope(pthread_attr_t const *attr);

int pthread_getattr_np(pthread_t thid, pthread_attr_t * attr);

三 线程参数传递

代码还是上一节的示例代码。 参数传递的是指针。我们将value的值传入。

pthread_create(&id,&attr,run,&value);

然后进行指针变量类型转换就可得到值。

int value=*(int *)ptr;

四 线程优先级

先上代码,这里相对于上一节就修改了 main方法,所以只贴出部分代码

int main(){
        int ret=0;
        int value=10;
        pthread_t id;
        pthread_attr_t attr;
        sched_param param;
        //初始化
        pthread_attr_init(&attr);
        //设置相关属性
        pthread_attr_setscope (&attr,PTHREAD_SCOPE_PROCESS);
        //获取线程优先级参数
        pthread_attr_getschedparam(&attr,&param);
        //设置优先级
        param.sched_priority=10;
        pthread_attr_setschedparam(&attr,&param);
        ret=pthread_create(&id,&attr,run,&value);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        return 0;
}

主要涉及sched_parampthread_attr_setschedparampthread_attr_getschedparam等方法。优先级变量存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。

五 线程的分离状态

线程的分离状态决定一个线程以什么样的方式来终止自己。 在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

六 线程的数据处理

6.1 线程数据

在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。 总结上面的理论:我们要在线程中使用全局变量,但是这个全局变量在各个线程中是独立的。 先上代码:

#include <iostream>
using namespace std;

//用户访问和获取线程变量。所有的线程都可以访问
pthread_key_t key;
void *run(void *ptr){
        int value=*(int *)ptr;
        int temp=1;
        //将temp的地址赋给key。
        pthread_setspecific (key, &temp);
        for(int i=0; i<3; i++) {
                //根据key获取对应的值
                int v=*(int *)pthread_getspecific(key);
                usleep(1000*100);
                cout<<"run key value>> "<<v<<endl<<flush;
        }
        return 0;
}
void *run2(void *ptr){
        int temp=2;
        pthread_setspecific (key, &temp);
        for(int i=0; i<3; i++) {
                int v=*(int *)pthread_getspecific(key);
                usleep(1000*150);
                cout<<"run2 key value>> "<<v<<endl<<flush;
        }
        return 0;
}

int main(){
        //创建key
        pthread_key_create(&key,NULL);
        int ret=0;
        int value=10;
        pthread_t id;
        pthread_attr_t attr;
        sched_param param;
        //初始化
        pthread_attr_init(&attr);
        //设置相关属性
        pthread_attr_setscope (&attr,PTHREAD_SCOPE_PROCESS);
        //获取线程优先级参数
        pthread_attr_getschedparam(&attr,&param);
        //设置优先级
        param.sched_priority=10;
        pthread_attr_setschedparam(&attr,&param);
        ret=pthread_create(&id,&attr,run,&value);
        if(ret) {
                cout<<"create thread failed "<<endl;
                return 0;
        }
        pthread_t id2;
        ret=pthread_create(&id2,NULL,run2,NULL);
        if(ret) {
                cout<<"create thread 2 failed "<<endl;
                return 0;
        }
        pthread_join(id,NULL);
        pthread_join(id2,NULL);
        pthread_key_delete(key);
        return 0;
}

相关的函数和结构有pthread.hpthread_key_createpthread_key_deletepthread_key_tpthread_setspecificpthread_getspecific

  • pthread_key_create 创建函数声明:
int pthread_key_create(pthread_key_t *key, void (*destructor_function)(void *))

函数 pthread_key_create() 用来创建线程私有数据。该函数从 TSD 池中分配一项,将其地址值赋给 key 供以后访问使用。

  • pthread_key_t key 指向一个键值的指针 pthread_key_t的定义为typedef int pthread_key_t;不论哪个线程调用了 pthread_key_create(),所创建的 key 都是所有线程可以访问的,但各个线程可以根据自己的需要往 key 中填入不同的值,相当于提供了一个同名而不同值的全局变量(这个全局变量相对于拥有这个变量的线程来说)。
  • destructor_function 这是一个销毁函数,它是可选的,可以为 NULL,为 NULL 时,则系统调用默认的销毁函数进行相关的数据注销。如果不为空,则在线程退出时(调用 pthread_exit() 函数)时将以 key 锁关联的数据作为参数调用它,以释放分配的缓冲区,或是关闭文件流等。
  • pthread_setspecific/pthread_getspecific 设置和获取线程变量的值。

七 互斥锁

互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。 先上代码,这里我们做了一个读写模型:

#include <iostream>
using namespace std;
char buffer;
int buffer_has_item=0;
pthread_mutex_t mutex;
void writerFunc(){
        while(1) {
                /* 锁定互斥锁*/
                pthread_mutex_lock (&mutex);
                buffer_has_item++;
                cout<<"write "<<buffer_has_item<<endl;
                /* 打开互斥锁*/
                pthread_mutex_unlock(&mutex);
                usleep(1000*200);
        }
}
void *readerFunc(void *ptr){
        while(1) {
                pthread_mutex_lock(&mutex);
                if(buffer_has_item>0) {
                        cout<<"read >>>> "<<buffer_has_item<<endl;
                        buffer_has_item--;
                }
                pthread_mutex_unlock(&mutex);
                usleep(1000*500);
        }
}
int main(){
        pthread_t id;
        pthread_mutex_init (&mutex,NULL);
        pthread_create(&id, NULL,readerFunc, NULL);
        writerFunc();
        return 0;
}

结果:

[root@localhost threadDemo]# ./second 
write 1
read >>>> 1
write 1
write 2
read >>>> 2
write 2
write 3
read >>>> 3
7.1 核心流程
  • 定义一个锁(pthread_mutex_t)
  • 初始化锁
  • 使用pthread_mutex_lock/pthread_mutex_unlock进行锁定和解锁。

先看下 互斥锁pthread_mutex_t的定义

typedef struct
{
    int volatile value;
} pthread_mutex_t;

使用前需要初始化互斥锁pthread_mutex_init

7.2 锁定和解锁

pthread_mutex_lock 声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁

7.3 互斥锁其他相关方法
  • pthread_mutexattr_setpshared设置属性pshared。可以取:
    • PTHREAD_PROCESS_SHARED 不同进程中的线程同步
    • PTHREAD_PROCESS_PRIVATE 同进程中的不同线程同步
  • pthread_mutexattr_settype 设置互斥锁类型 其他:
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int  pshared);
int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared);

int pthread_mutex_init(pthread_mutex_t *mutex,
                       const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
#if 0 /* MISSING FROM BIONIC */
int pthread_mutex_timedlock(pthread_mutex_t *mutex, struct timespec*  ts);
#endif /* MISSING */

八 条件变量

前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。对于消费者,它根本不知道什么时候生产者已经生产了数据,只能通过轮询来检测,这就有两个缺陷:

    1. 轮询会占用CPU资源
    1. 轮询的时间不好控制,可能导致消费者执行不及时。

为了解决上面这个问题,我们这样考虑。费线程在阻塞之前要先解锁(个人想法:消费线程已经获得了要访问资源的锁,但是,即使我获得了资源的锁,但是由于条件暂时还不满足,我无法用这个资源,所以我想暂时让出这把锁,让之里的资源暂时为别人所用,所以在挂起前,我需要解锁),同时还要将自己的标识符放入一个地方,以便生产线程通过这个标识符来激活自己。那新问题又来了,由于线程之间是并发/并行的。消费线程可能刚完成解锁的操作,就被生产线程获取到了并开始执行,这时,因为消费线程还未挂起自己,来不及将自己的标识符保存在某个位置,所以生产线程不认为有正在等待的线程(生产线程想告诉消费线程的唯一方式就是认消费线程的标识符)。这时,切换到消费线程后,消费线程将永远的等待下去,虽然队列中有产品,但生产线程也不会告诉消费线程。而生产线程因为队列中有产品可能也一直的等待下去,形成了死锁。

这里死锁的原因很明确,就是因为消费线程在阻塞之前要先解锁解、保存线程标识符、挂起这一系列操作不是原子操作。想要让这一些列的操作成为原子操作,就得引入条件变量,所以不难想到使用条件变量的时候必须要“伴随”一个互斥量。

条件变量是与互斥量相关联的一种用于多线程之间关于共享数据状态改变的通信机制。它将解锁和挂起封装成为原子操作。等待一个条件变量时,会解开与该条件变量相关的锁,因此,使用条件变量等待的前提之一就是保证互斥量加锁。线程醒来之后,该互斥量会被自动加锁,所以,在完成相关操作之后需要解锁。

用条件变量配合互斥量实现,条件变量与互斥量结合,使得在条件不满足的情况下,能够释放对缓冲区的占用,使得他人能够访问缓冲区。当我添加满足时,我又可以及时的加锁之后独占资源的完成我自己的工作。

我们先上测试代码:

#include <iostream>
using namespace std;
int buffer_has_item=0;
pthread_mutex_t mutex;
pthread_cond_t count_nonzero;
void writerFunc(){
        while(1) {
                pthread_mutex_lock (&mutex);
                buffer_has_item+=2;
                cout<<"write "<<buffer_has_item<<endl;
                pthread_mutex_unlock(&mutex);
                //激活阻塞的读线程
                pthread_cond_signal(&count_nonzero);
                usleep(1000*200);
        }
}
void *readerFunc(void *ptr){
        while(1) {
                pthread_mutex_lock(&mutex);
                if(buffer_has_item<=0) {
                        //暂时解锁,把资源让出,等待写线程写入。
                        //被激活后会自动加锁
                        pthread_cond_wait( &count_nonzero, &mutex);
                }
                cout<<"read >>>> "<<buffer_has_item<<endl;
                buffer_has_item--;
                pthread_mutex_unlock(&mutex);
        }
}
int main(){
        pthread_t id;
        pthread_mutex_init (&mutex,NULL);
        pthread_create(&id, NULL,readerFunc, NULL);
        writerFunc();
        return 0;
}

主要涉及内容有pthread_cond_tpthread_cond_signalpthread_cond_wait

pthread_condattr_t的定义为

typedef long pthread_condattr_t;

用来定义条件变量。

  • pthread_cond_wait 线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒。,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否<=0等等
  • pthread_cond_signal 用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。

其他相关函数:

int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_getpshared(pthread_condattr_t *attr, int *pshared);
int pthread_condattr_setpshared(pthread_condattr_t* attr, int pshared);
int pthread_condattr_destroy(pthread_condattr_t *attr);

int pthread_cond_init(pthread_cond_t *cond,
                      const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(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);

/* BIONIC: same as pthread_cond_timedwait, except the 'abstime' given refers
 *         to the CLOCK_MONOTONIC clock instead, to avoid any problems when
 *         the wall-clock time is changed brutally
 */
int pthread_cond_timedwait_monotonic_np(pthread_cond_t         *cond,
                                        pthread_mutex_t        *mutex,
                                        const struct timespec  *abstime);

/* BIONIC: DEPRECATED. same as pthread_cond_timedwait_monotonic_np()
 * unfortunately pthread_cond_timedwait_monotonic has shipped already
 */
int pthread_cond_timedwait_monotonic(pthread_cond_t         *cond,
                                     pthread_mutex_t        *mutex,
                                     const struct timespec  *abstime);

#define HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC 1

/* BIONIC: same as pthread_cond_timedwait, except the 'reltime' given refers
 *         is relative to the current time.
 */
int pthread_cond_timedwait_relative_np(pthread_cond_t         *cond,
                                     pthread_mutex_t        *mutex,
                                     const struct timespec  *reltime);

#define HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE 1



int pthread_cond_timeout_np(pthread_cond_t *cond,
                            pthread_mutex_t * mutex,
                            unsigned msecs);

九 信号量

线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。 信调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。 老规矩,先上代码,跑起来再说:

#include <iostream>
#include <semaphore.h>
#include <string.h>
#include <stdio.h>
using namespace std;
//信号量
sem_t sem;
void * run(void *ptr){
        char *buf=(char *)ptr;
        while(strcmp("exit\n",buf)!=0) {
                //新号量-1
                sem_wait(&sem);
                cout<<"thread output>> "<<buf<<endl<<flush;
        }
        return 0;
}

int main(){
        char buf[100]={0};
        int ret=0;
        pthread_t id;
        //初始化信号量数量为2。默认类型,非0位进程间共享
        ret=sem_init(&sem, 0, 2);
        if(ret) {
                cout<<"sem_init failed"<<endl;
        }
        pthread_create(&id,NULL,run,buf);
        //循环从标准输入读(fgets会将\n也读入)
        while(fgets(buf,sizeof(buf),stdin)) {
                if(strcmp("exit\n",buf)==0) {
                        break;
                }
                //信号量+1
                sem_post(&sem);
        }
        //清理信号量
        sem_destroy(&sem);
        return 0;
}

函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数,它们都在头文件/usr/include/semaphore.h中定义。

#include <sys/cdefs.h>

__BEGIN_DECLS

typedef struct {
    volatile unsigned int  count;
} sem_t;

#define  SEM_FAILED  NULL

extern int sem_init(sem_t *sem, int pshared, unsigned int value);

extern int    sem_close(sem_t *);
extern int    sem_destroy(sem_t *);
extern int    sem_getvalue(sem_t *, int *);
extern int    sem_init(sem_t *, int, unsigned int);
extern sem_t *sem_open(const char *, int, ...);
extern int    sem_post(sem_t *);
extern int    sem_trywait(sem_t *);
extern int    sem_unlink(const char *);
extern int    sem_wait(sem_t *);

struct timespec;
extern int    sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

结果:

[root@localhost threadDemo]# ./fourth 
thread output>> 
thread output>> 
hello
thread output>> hello

come on baby
thread output>> come on baby

exit

这里需要引入新的头文件semaphore.h

  • sem_init 初始化信号量。该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则信号量就可以在多个进程之间共享,value为sem的初始值。调用成功时返回0,失败返回-1.
  • sem_post ( sem_t *sem ) 该函数用于以原子操作的方式将信号量的值加1。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
  • sem_wait( sem_t *sem ) 被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减1,表明公共资源经使用后减少。
  • sem_destroy 该函数用于对用完的信号量的清理

semaphore.h头文件

#ifndef _SEMAPHORE_H
#define _SEMAPHORE_H    1

#include <features.h>
#include <sys/types.h>
#ifdef __USE_XOPEN2K
# define __need_timespec
# include <time.h>
#endif

/* Get the definition for sem_t.  */
#include <bits/semaphore.h>


__BEGIN_DECLS

/* Initialize semaphore object SEM to VALUE.  If PSHARED then share it
   with other processes.  */
extern int sem_init (sem_t *__sem, int __pshared, unsigned int __value)
     __THROW;
/* Free resources associated with semaphore object SEM.  */
extern int sem_destroy (sem_t *__sem) __THROW;

/* Open a named semaphore NAME with open flags OFLAG.  */
extern sem_t *sem_open (__const char *__name, int __oflag, ...) __THROW;

/* Close descriptor for named semaphore SEM.  */
extern int sem_close (sem_t *__sem) __THROW;

/* Remove named semaphore NAME.  */
extern int sem_unlink (__const char *__name) __THROW;

/* Wait for SEM being posted.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int sem_wait (sem_t *__sem);

#ifdef __USE_XOPEN2K
/* Similar to `sem_wait' but wait only until ABSTIME.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int sem_timedwait (sem_t *__restrict __sem,
              __const struct timespec *__restrict __abstime);
#endif

/* Test whether SEM is posted.  */
extern int sem_trywait (sem_t *__sem) __THROW;

/* Post SEM.  */
extern int sem_post (sem_t *__sem) __THROW;

/* Get current value of SEM and store it in *SVAL.  */
extern int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval)
     __THROW;


__END_DECLS

#endif  /* semaphore.h */

参考博客:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Linux驱动

41.Linux应用调试-修改内核来打印用户态的oops

1.在之前第36章里,我们学习了通过驱动的oops定位错误代码行 第36章的oops代码如下所示: Unable to handle kernel paging...

2995
来自专栏别先生

JSch - Java实现的SFTP(文件上传详解篇)

  JSch是Java Secure Channel的缩写。JSch是一个SSH2的纯Java实现。它允许你连接到一个SSH服务器,并且可以使用端口转发,X11...

1K1
来自专栏ImportSource

学会一个JVM插件:使用HSDIS反汇编JIT生成的代码

HSDIS是一个Java官方推荐 HotSpot虚拟机JIT编译代码的反汇编插件。我们有了这个插件后,通过JVM参数-XX:+PrintAssembly就可以加...

86611
来自专栏区块链

GoAHEAD嵌入式WEB SERVER漏洞概览

GOAHEAD由于开源、简单、轻巧、功能强大、多平台等特点,主要运用于嵌入式设备上。考虑到如路由器等等的使用场景等加上出现的漏洞情况等等,使得其漏洞利用难度也会...

2586
来自专栏Golang语言社区

【Go 语言社区】Go文件操作函数

文件操作 golang的文件操作时在os包中的。 所以用的时候要import os包 包含以下的这些函数 func Create(n...

3036
来自专栏无题

《深入理解JVM》阅读笔记以及问题整理

对阅读周志明先生的《深入理解JVM》产生的疑问与感悟以及要点进行总结。 想这种技术书应该反复读,最近又阅览了一次,才对GC部分有了一个大概的框架,可是细节部分依...

3506
来自专栏我的博客

$_PUT?put数据获取

我们经常使用$_GET和$_POST来进行服务器交互,但是我们有的时候不得不被逼使用$_PUT方法获取数据 当然,php中是没有$_PUT的,但是我们可以使用...

6326
来自专栏开发 & 算法杂谈

Zookeeper C API学习总结

客户端使用C语言开发,zookeeper提供了两个库,zookeeper_st(单线程库)以及zookeeper_mt(多线程库)。

9895
来自专栏PPV课数据科学社区

python多线程编程(1): python对多线程的支持

前面介绍过多线程的基本概念,理解了这些基本概念,掌握python多线程编程就比较容易了。 在开始之前,首先要了解一下python对多线程的支持。 虚拟机层面 P...

37915
来自专栏养码场

Java后端开发面大集锦1.0,汇集了各大公司的面试点!你都能答上来吗?

各位在面试时,必然会遇到各位的技术问题。这次针对Java后端开发,以下这篇文章罗列了各大公司技术面试官可能会提及的问题,并做出了解答。若觉得不错,希望分享给更多...

643

扫码关注云+社区

领取腾讯云代金券