Linux同步与互斥

目录

1、简介

2、Linux互斥锁

 2.1、简介

 (1)概念

 (2)死锁

 (3)实例演示

 2.2、常用函数

 (1)pthread_mutex_init

 (2)pthread_mutex_lock

 (3)pthread_mutex_trylock

 (4)pthread_mutex_unlock

 (5)pthread_mutex_destroy

3、Linux条件变量

 3.1、简介

 (1)概念

 (2)实例演示

 3.2、常用函数

 (1)pthread_cond_init

 (2)pthread_cond_wait

 (3)pthread_cond_signal

 (4)pthread_cond_broadcast

 (5)pthread_cond_destroy

4、信号量

 4.1、简介

 (1)概念

 (2)实例演示1

 (3)实例演示2

 4.2、POSIX常用函数

 (1)sem_init

 (2)sem_open

 (3)sem_wait

 (4)sem_trywait

 (5)sem_post

 (6)sem_close

 (7)sem_destroy


1、简介

 (1)概念

同步和互斥是并发编程中的两个重要概念,它们通常在多线程或多进程环境中用于协调对共享资源的访问,以避免数据不一致和竞争问题。具体如下:

    ① 同步是指控制线程的执行顺序,以满足先后顺序。

    ② 互斥指的是确保在任何给定时刻只有一个线程能够访问共享资源,来避免竞争条件和数据不一致问题。

在C标准库中没有提供同步与互斥相关的功能,而是在Linux系统调用和Windows系统调用中实现的。另外,一些框架(如QT)也有自己的进程实现。C++11也引入了互斥锁相关的概念。

常见的同步与互斥机制如下:

    ① 互斥锁:确保同时只有一个线程可以访问临界区或共享资源,用于防止数据竞争。

    ② 条件变量:配合互斥锁使用,允许线程等待某个条件满足时再继续执行,可以显著降低CPU的消耗。通常用于实现复杂的等待/通知机制。

    ③ 信号量:控制多个线程同时访问资源的数量,常用于实现计数器、限流等功能。

这几个机制都能实现基础的互斥需求,每种机制同时使用多个对象就可以实现同步需求。

2、Linux互斥锁
 2.1、简介
 (1)概念

    在Linux中,互斥锁(Mutex Lock)是一种用于多线程编程的同步原语,确保同时只有一个线程可以访问临界区或共享资源,用于防止数据竞争。
    互斥锁是一种二进制信号量,它有两种状态:锁定(Locked)和解锁(Unlocked)。当一个线程持有锁时,其他线程试图获取该锁时会被阻塞,直到该锁被释放。它的基本操作如下:
    ① 创建一个全局的互斥锁对象,即pthread_mutex_t类型的全局变量。
    ② 初始化锁:使用 pthread_mutex_init() 函数初始化互斥锁。
    ③ 加锁:使用 pthread_mutex_lock()或pthread_mutex_trylock() 函数来获取互斥锁。如果互斥锁已经被其他线程持有,那么当前线程会被阻塞,直到获取到互斥锁为止。
    ④ 操作共享资源:在互斥锁保护的临界区内对共享资源进行操作。
    ⑤ 解锁:使用 pthread_mutex_unlock() 函数释放互斥锁,使其他线程可以获取该锁。
    ⑥ 回收资源:使用pthread_mutex_destroy()释放互斥锁。
    这些函数的头文件为<pthread.h>。

 (2)死锁

    死锁是指两个或多个进程(或线程)互相持有对方所需的资源,导致它们都无法继续执行。这种情况下,每个进程都在等待其他进程释放资源,而自己却不释放已经持有的资源,结果导致所有进程都无法继续执行下去。
    死锁的产生通常由于以下四个必要条件的同时满足:
    ① 互斥条件:资源只能被一个进程(线程)持有,如果一个进程已经获得了某个资源,其他进程就无法再次获取它,只能等待。
    ② 请求与保持条件:一个进程在持有至少一个资源的同时又提出了新的资源请求,而该资源已被其他进程占有,导致该进程被阻塞。
    ③ 不剥夺条件:已经分配给进程的资源不能被强制性地剥夺,只能由占有它的进程自行释放。
    ④ 循环等待条件:存在一个进程链,每个进程都在等待下一个进程所持有的资源,形成一个循环等待的局面。

    因此,要解决死锁问题,通常需要打破死锁产生的必要条件之一。常见的方法包括资源预分配、避免循环等待、引入超时机制等,具体如下:
    ① 设计资源编号
这种方法是指设计一个协议,按照固定的顺序来请求资源。例如,系统中的所有资源都可以被编号,并且规定进程只能按照编号顺序来申请资源。这种方法确保了不会形成环形链,从而打破了循环等待条件。
    ② 资源预分配和资源分配图
在这种策略中,进程在开始执行之前必须一次性申请其所需的所有资源。如果不可能一次性分配所有资源,则不会启动该进程。这种方法通过避免请求和保持条件来预防死锁。系统可以使用资源分配图来检测分配请求是否会导致死锁,如果会,就不允许这种分配。
    ③ 资源剥夺和回滚
当系统检测到死锁或者预测到将要发生死锁时,可以采取行动从一个或多个进程中剥夺资源,将其分配给其他进程。剥夺后的进程可能会被回滚到某个安全点并重新执行。这种方法涉及到修改不剥夺条件。
    ④ 超时和重试机制
在某些系统中,可以给资源请求设置超时时间。如果进程在指定时间内无法获取所有必需的资源,则它必须释放已经占有的资源,并在稍后重试。这种方法通过周期性地放弃和重新请求资源来避免死锁。

 (3)实例演示

    一个使用互斥锁的简单例子如下:

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void *thread_function(void *arg) {
    // 尝试锁定互斥量
    pthread_mutex_lock(&lock);
    printf("Thread has locked the mutex.\n");

    // 执行关于共享资源的操作
    // ...

    // 解锁互斥量
    pthread_mutex_unlock(&lock);
    printf("Thread has unlocked the mutex.\n");
    return NULL;
}

int main() {
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, thread_function, NULL);
    pthread_create(&tid2, NULL, thread_function, NULL);

    // 等待线程完成
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    // 销毁互斥量
    pthread_mutex_destroy(&lock);

    return 0;
}
 2.2、常用函数
 (1)pthread_mutex_init

    ① 原型:

1.int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

    ② 功能:初始化互斥锁。如果使用PTHREAD_MUTEX_INITIALIZER对互斥锁进行初始化,则表示该锁是静态分配的,不需要再调用此函数进行初始化,锁使用的是默认属性。但如果我们需要其他类型的互斥锁(如递归锁),则需要调用此函数并提前设置相应的属性。

1.pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

    ③ 参数:

a)mutex: 指向要初始化的互斥锁的指针。

b)attr: 指向一个互斥锁属性对象的指针。如果传递 NULL,则使用默认属性。在大多数情况下,将属性参数设置为NULL与直接使用 PTHREAD_MUTEX_INITIALIZER 进行初始化的效果是相同的。pthread_mutexattr_t类型不能像结构体那样直接赋值,而是要像互斥锁一样使用函数赋值,具体来讲,要使用pthread_mutexattr_init对属性进行初始化,再用pthread_mutexattr_settype设置属性,最后用pthread_mutexattr_destroy销毁属性。

④ 返回值:成功返回0,失败返回非0。

   pthread_mutexattr_t attr;
    pthread_mutex_t lock;
    int result;

    // 初始化互斥锁属性
    result = pthread_mutexattr_init(&attr);
    if (result != 0) {
        printf("Mutex attribute init failed\n");
        return 1; // 错误处理
    }

    // 设置互斥锁属性为错误检查类型
    result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
    if (result != 0) {
        printf("Mutex attribute set type failed\n");
        pthread_mutexattr_destroy(&attr);
        return 1; // 错误处理
    }

    // 初始化互斥锁,使用自定义属性
    result = pthread_mutex_init(&lock, &attr);
    ...
    // 销毁互斥锁和属性
    pthread_mutex_destroy(&lock);
    pthread_mutexattr_destroy(&attr);
 (2)pthread_mutex_lock

    ① 原型:int pthread_mutex_lock(pthread_mutex_t *mutex);

    ② 功能:用于锁定一个互斥量。如果互斥量已经被锁定,调用此函数的线程将会阻塞,直到该互斥量被解锁。

    ③ 返回值:成功返回0,失败返回错误码,如 EDEADLK(已经由调用线程锁定)和 EINVAL(互斥锁未正确初始化)。

    ④ 注意:尝试锁定一个已经由调用线程锁定的互斥量将导致死锁,除非该互斥量是递归类型。

 (3)pthread_mutex_trylock

    ① 原型:int pthread_mutex_trylock(pthread_mutex_t *mutex);

    ② 功能:尝试获取互斥锁。该函数会立即返回,无论其是否成功获取了互斥锁。适用于那些不能阻塞等待互斥锁的场景。例如,一个线程需要处理多个资源,但不希望因为某个资源的互斥锁而阻塞,影响对其他资源的处理。使用 pthread_mutex_trylock 可以立即知道是否可以继续操作该资源,或者需要跳过以避免延迟。

    ③ 返回值:成功获取互斥锁返回0,失败返回非0。

 (4)pthread_mutex_unlock

    ① 原型:int pthread_mutex_unlock(pthread_mutex_t *mutex);

    ② 功能:释放已经被当前线程持有的互斥锁。该函数使其他可能在等待此互斥锁的线程能够继续执行。

    ③ 返回值:成功返回0,失败返回非0。

 (5)pthread_mutex_destroy

    ① 原型:int pthread_mutex_destroy(pthread_mutex_t *mutex);

    ② 功能:销毁一个互斥锁。销毁互斥锁主要是为了回收与互斥锁相关的资源。

    ③ 返回值:成功返回0,失败返回非0。

    ④ 注意:销毁前要确保没有线程在使用该互斥锁。

3、Linux条件变量
 3.1、简介
 (1)概念

在Linux系统中,条件变量是与互斥锁结合使用的同步原语。它提供了一种方式,使得线程可以睡眠等待某个条件成立,而不是在某个循环中不断检查该条件是否成立,这样可以显著降低CPU的消耗。通过组合使用多个条件变量,可以实现线程同步需求。条件变量可以看作是互斥锁的一个补充,用于更加灵活地控制线程的等待和唤醒。

使用条件变量进行线程同步的基本步骤如下:

    ① 初始化互斥锁与条件变量。条件变量通常与互斥锁连用。这些初始化操作通常在全局进行,可以直接进行默认初始化,也可以使用pthread_cond_init进行详细配置。

1.pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

2.pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    ② 锁定互斥锁:在访问与条件变量相关的共享数据之前,线程必须先锁定互斥锁。这可以保证数据访问的原子性和一致性。

    ③ 检查条件并等待条件变量:在条件变量的基本使用模式中,线程在继续执行前需检查某个条件是否满足。如果条件未满足,则线程会调用 pthread_cond_wait 函数来等待条件变量。此函数会自动释放互斥锁并使线程进入阻塞状态,当条件变量被触发时(即被其他线程唤醒),pthread_cond_wait 会自动重新获得互斥锁并返回。

1.while (!ready) {

2.    pthread_cond_wait(&cond, &lock);

3.}

    ④ 修改条件并唤醒条件变量:在修改与条件变量相关的共享数据后(通常由“生产者”线程执行),需要通过 pthread_cond_signal 或 pthread_cond_broadcast 来唤醒一个或所有等待的线程。

1.ready = 1; // 修改条件

2.pthread_cond_signal(&cond); // 唤醒等待的线程

    ⑤ 解锁互斥锁:无论是等待条件变量还是唤醒条件变量后,都需要释放互斥锁,以便其他线程可以进入临界区。

    ⑥ 销毁资源:当条件变量和互斥锁不再需要时,应当对它们进行销毁,释放相关资源。使用pthread_cond_destroy销毁条件变量。

 (2)实例演示

一个使用条件变量实现的“生产者-消费者”模型如下:

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0; // 条件变量

void *producer(void *arg) {
    pthread_mutex_lock(&lock);
    // 生产者完成工作
    printf("Producer done.\n");
    ready = 1; // 设置条件
    pthread_cond_signal(&cond); // 通知等待的线程
    pthread_mutex_unlock(&lock);
    return NULL;
}

void *consumer(void *arg) {
    pthread_mutex_lock(&lock);
    while (!ready) {
        pthread_cond_wait(&cond, &lock); // 等待条件变量
    }
    printf("Consumer proceed.\n");
    pthread_mutex_unlock(&lock);
    return NULL;
}

int main() {
    pthread_t th1, th2;
    pthread_create(&th1, NULL, producer, NULL);
    pthread_create(&th2, NULL, consumer, NULL);
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);
    return 0;
}
 3.2、常用函数
 (1)pthread_cond_init

    ① 原型:int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

    ② 功能:初始化条件变量。与互斥锁类似,如果使用 PTHREAD_COND_INITIALIZER 来定义条件变量,表示对它进行了静态初始化,就不需要再调用此函数来进行初始化了。

    ③ 参数:att用来设置条件变量的属性。如果传入 NULL,则使用默认属性。如果不想使用默认属性,需要先用pthread_condattr_init 函数初始化属性对象,再用pthread_condattr_setpshared设置属性,最后在进程结束时使用pthread_condattr_destroy 销毁属性。

    ④ 返回值:成功返回0,失败返回错误码,比如 ENOMEM(内存不足)或 EINVAL(无效的参数)。

 (2)pthread_cond_wait

    ① 原型:

1.int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

    ② 功能:用于阻塞当前线程,直到指定的条件变量被触发。这个函数通常与互斥锁(mutex)一起使用,以避免竞争条件和保证线程安全。在调用此函数之前,需要确保调用线程已经成功锁定了互斥锁。调用后,此函数会自动释放互斥锁并使线程进入等待状态。只有当条件变量被其他线程通过 pthread_cond_signal 或 pthread_cond_broadcast 唤醒时,等待的线程才会重新激活,此函数才会返回。返回后,互斥锁会再次被该线程锁定,因此在该函数之后通常需要再次检查条件是否真正满足。

1.    while (!ready) {

2.        pthread_cond_wait(&cond, &lock); // 等待条件变量

3.    }

    ③ 返回值:成功返回0,失败返回错误码,如EINVAL表示传递给函数的互斥锁或条件变量未正确初始化。EPERM表示调用线程未锁定指定的互斥锁。

    ④ 注意:线程可能会在没有接收到通知的情况下醒来(所谓的“虚假唤醒”),因此 wait 应该总是在一个循环中调用,并且条件应该在循环中检查。

 (3)pthread_cond_signal

    ① 原型:int pthread_cond_signal(pthread_cond_t *cond);

    ② 功能:向一个等待在指定条件变量上的线程发送通知,使其能继续执行。如果存在多个等待的线程,系统选择其中一个。

    ③ 返回值:成功返回0,失败返回错误码。

    ④ 注意:使用时,建议持有相关互斥锁,这样可以确保唤醒操作和状态变更操作的原子性,避免产生竞争条件。

1.pthread_mutex_lock(&mutex);     // 锁定互斥锁

2.ready = 1;                      // 更改条件

3.pthread_cond_signal(&cond);     // 通知等待cond的线程

4.pthread_mutex_unlock(&mutex);   // 释放互斥锁

 (4)pthread_cond_broadcast

    ① 原型:int pthread_cond_broadcast(pthread_cond_t *cond);

    ② 功能:用于唤醒所有等待特定条件变量的线程。

    ③ 返回值:成功:函数返回 0。失败:返回一个错误码,表明无法广播信号。

 (5)pthread_cond_destroy

    ① 原型:int pthread_cond_destroy(pthread_cond_t *cond);

    ② 功能:用于销毁已经初始化的条件变量。当条件变量不再被使用时,应该调用这个函数来释放与之关联的资源。

    ③ 返回值:成功:函数返回 0。失败:返回一个错误码,表明条件变量无法被销毁。

    ④ 注意:在销毁条件变量前,应确保没有线程正在该条件变量上等待。尝试销毁一个正在被等待的条件变量可能导致未定义行为。

4、信号量
 4.1、简介
 (1)概念

信号量是一种用于控制多个线程同时访问资源的数量的同步机制。

信号量是一个整数值,它通常用来表示一个系统中可用资源的数量。信号量的操作主要有两种:等待(wait)和信号(signal),这些操作在不同系统中可能被称为P(proberen, 测试)和V(verhogen, 增加)操作。具体如下:

    ① 等待(P操作):当一个进程或线程需要一个资源时,它会执行一个等待操作。这个操作会检查信号量的值,如果信号量的值大于零,表示有资源可用,信号量的值会减一,进程或线程继续执行。如果信号量的值为零,表示没有可用资源,进程或线程将进入等待状态,直到信号量的值大于零。

    ② 信号(V操作):当进程或线程释放资源时,会执行一个信号操作。这个操作会将信号量的值加一。如果有其他进程或线程正在等待这个信号量,这些等待的进程或线程可能会被唤醒,以获取刚释放的资源。

信号量可以分为两大类型:

    ① 二进制信号量(或互斥信号量):其值只能是0或1,用于控制对单一共享资源的访问,实现互斥锁的功能。

    ② 计数信号量:可以有大于1的值,用于控制对一组相同资源的访问,例如管理有限数量的数据库连接或线程池中的线程。

在UNIX或类UNIX系统中,信号量通常是通过System V IPC(Inter-Process Communication)机制或POSIX信号量来实现的。

System V 信号量是最早的信号量实现之一,它的基本操作如下:

    ③ 用semget()创建或访问一个信号量集。信号量集中可以包含多个信号量。

    ④ 用semop()对信号量进行操作,如增加或减少信号量的值。操作是原子的,这意味着在一个操作中可以对多个信号量进行更改。

    ⑤ 用semctl()控制信号量,例如设置信号量的值,获取信号量的当前值或删除信号量集。

POSIX信号量是较新的标准,提供了一种更现代的方法来使用信号量。POSIX 信号量分为两类:无名信号量和命名信号量。

无名信号量是只存在于进程的内存空间中的信号量。它通常用于同一个进程中的不同线程之间进行同步,也可以在共享内存中用于不同的进程之间同步。无名信号量由 sem_init 函数进行初始化,传入的参数决定信号量是否在共享内存中(用于进程间同步)或者在局部进程内存中(用于线程间同步)。它的基本操作如下:

    ① 初始化:通过 sem_init 函数对全局信号量进行初始化。

    ② 使用:使用 sem_wait 和 sem_post 进行等待和释放操作。

    ③ 销毁:通过 sem_destroy 进行销毁。

命名信号量通过系统中的全局名字标识,允许不同的进程之间通过名称来进行信号量的同步。它的基本操作如下:

    ① 初始化:通过 sem_open 函数打开或创建一个信号量,传入一个唯一的名称。

    ② 使用:与无名信号量相同,通过 sem_wait 和 sem_post 进行等待和释放操作。

    ③ 关闭:通过 sem_close 关闭信号量的引用。

    ④ 删除:通过 sem_unlink 删除命名信

 (2)实例演示1

使用信号量的简单例子如下:

sem_t sem;
// 线程函数
void* thread_func(void* arg) {
    sem_wait(&sem);
    printf("Entered..\n");
    // 临界区代码
    sleep(2);
    printf("Exiting..\n");
    sem_post(&sem);
}

int main() {
    pthread_t t1, t2;
    sem_init(&sem, 0, 1); // 初始化信号量,最初为1

    pthread_create(&t1, NULL, thread_func, NULL);
    pthread_create(&t2, NULL, thread_func, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    sem_destroy(&sem); // 销毁信号量
    return 0;
}
 (3)实例演示2

使用命名信号量实现的一个简单的生产者-消费者模型如下,此示例中有两个程序:一个作为生产者增加信号量值,另一个作为消费者减少信号量值,生产者程序会每隔一秒生产一个项目,消费者程序会在信号量增加时消费相应的项目。

生产者程序 (producer.c):

#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>  // For O_* constants
#include <sys/stat.h>
#include <unistd.h>

#define SEM_NAME "/example_sem"

int main() {
    sem_t *sem = sem_open(SEM_NAME, O_CREAT, 0644, 0);  // 创建或打开一个命名信号量,初始值为0
    if (sem == SEM_FAILED) {
        perror("sem_open");
        return 1;
    }

    printf("Producing items...\n");
    for (int i = 0; i < 5; ++i) {
        sleep(1);
        sem_post(sem);  // 增加信号量的值
        printf("Produced item %d\n", i + 1);
    }

    sem_close(sem);
    return 0;
}

消费者程序 (consumer.c):

#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>  // For O_* constants

#define SEM_NAME "/example_sem"

int main() {
    sem_t *sem = sem_open(SEM_NAME, 0);  // 以只读方式打开已存在的命名信号量
    if (sem == SEM_FAILED) {
        perror("sem_open");
        return 1;
    }

    printf("Consuming items...\n");
    for (int i = 0; i < 5; ++i) {
        sem_wait(sem);  // 减少信号量的值,阻塞直到值大于0
        printf("Consumed item %d\n", i + 1);
    }

    sem_close(sem);
    sem_unlink(SEM_NAME);  // 删除命名信号量
    return 0;
}
 4.2、POSIX常用函数
 (1)sem_init

    ① 原型:int sem_init(sem_t *sem, int pshared, unsigned int value);

    ② 功能:用于创建并初始化一个无名信号量。头文件 <semaphore.h>。

    ③ 参数:

a)sem:指向要初始化的信号量的指针。

b)pshared:指定信号量的类型,如果为0,则表示该信号量只能用于线程间同步(进程内部共享),如果为非0,则表示该信号量可用于进程间同步(进程间共享)。

c)value:指定信号量的初始值。通常初始化为1。

    ④ 返回值:成功返回0,失败返回-1,并设置错误码。

    ⑤ 注意:无名信号量的生命周期通常与进程或线程的生命周期相关联。因此,在销毁进程或线程时,通常需要销毁相关的无名信号量,以避免资源泄漏。

 (2)sem_open

    ① 原型:sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

    ② 功能:用于创建或打开一个命名信号量。头文件 <semaphore.h>。

    ③ 参数:

a)name:信号量的名称。在系统中,信号量通过名称来标识。如果要在不同的进程之间共享信号量,必须使用命名信号量。对于未命名的信号量,可以传递 NULL。通常,这个名称会以斜线开头,类似于文件系统的路径,例如/my_semaphore。

b)oflag:用于指定信号量的打开方式,它可以是以下值的按位或组合:

1.O_CREAT:如果信号量不存在,则创建它。

2.O_EXCL:与 O_CREAT 一起使用时,如果指定信号量已存在,则报错。

c)mode:用于指定新创建的信号量的权限,仅在创建信号量时有效,通常为0600或0644。

d)value:信号量的初始值。对于二进制信号量,可以为 0 或 1;对于计数信号量,可以是任何非负整数。

    ④ 返回值:成功,返回指向新创建或打开的信号量的指针 sem_t *。如果出错,返回 SEM_FAILED 宏,错误码会设置为适当的错误值。

    ⑤ 注意:sem_open 函数创建或打开的信号量通常使用 sem_close 和 sem_unlink 函数进行关闭和删除。

 (3)sem_wait

    ① 原型:int sem_wait(sem_t *sem);

    ② 功能:对信号量的值进行减1操作。如果信号量的当前值大于0,那么这个减1操作会立即执行。如果信号量的当前值为0,则 sem_wait 函数会导致调用线程阻塞,直到信号量的值变为大于0。sem_wait 函数内部采用原子操作来修改信号量的值,确保在多线程环境中操作的安全性和正确性。大多数现代操作系统实现的信号量机制都会尽量保证线程唤醒的公平性,即按照线程进入阻塞状态的顺序来唤醒它们。头文件 <semaphore.h>。

 (4)sem_trywait

    ① 原型:int sem_trywait(sem_t *sem);

    ② 功能:尝试对信号量的值进行减1操作。与 sem_wait 不同的是,如果信号量的当前值为0,则 sem_trywait 函数不会阻塞调用线程,而是立即返回一个错误码。头文件 <semaphore.h>。

    ③ 返回值:成功执行减1操作,则返回值为0,表示成功。如果信号量的当前值为0,函数会返回一个非零值,并设置错误码为 EAGAIN,表示资源暂时不可用。

 (5)sem_post

    ① 原型:int sem_post(sem_t *sem);

    ② 功能:将信号量的值加 1。如果有其他线程正在等待这个信号量,那么增加信号量值后会唤醒其中一个等待线程。头文件 <semaphore.h>。

    ③ 返回值:成功返回0,失败返回-1,并设置错误码。

    ④ 注意:对于二进制信号量(binary semaphore),sem_post 只能增加信号量的值到 1。对于计数信号量(counting semaphore),sem_post 可以增加信号量的值到任意正整数。如果信号量的初始化值大于 1,它通常被视为计数信号量。这表明有多个相同的资源可用,适用于资源池的情况。

 (6)sem_close

    ① 原型:int sem_close(sem_t *sem);

    ② 功能:用于关闭一个打开的命名信号量。

    ③ 返回值:成功返回0,失败返回-1,并设置错误码。

    ④ 注意:如果在关闭信号量之前未解除该信号量的所有锁定,则行为是未定义的。因此,在关闭信号量之前,应该确保已解除所有相关的锁定。

 (7)sem_destroy

    ① 原型:int sem_destroy(sem_t *sem);

    ② 功能:销毁未命名的信号量。

    ③ 返回值:成功返回0,失败返回-1,并设置错误码。

    ④ 注意:在调用 sem_destroy 之前,应确保没有其他线程正在该信号量上等待。同时,在销毁信号量后继续使用它是不安全的。

    ① 原型:int sem_unlink(const char *name);

    ② 功能:用于删除一个命名信号量,以便释放系统资源并将其从文件系统中移除。

    ③ 返回值:成功返回0,失败返回-1,并设置错误码。

注意:在调用 sem_unlink 之前,应确保没有其他进程或线程正在使用该信号量。如果仍有其他进程或线程在使用该信号量,那么删除操作可能会失败。

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值