多进程编程- POSIX无名信号量

基本概念

无名信号量(也称为匿名信号量)是一个同步原语,通常用于线程之间的同步,而不是进程之间。与命名信号量(用于进程间同步)相比,无名信号量的生命周期通常受限于创建它的进程,并且它们不需要一个与系统相关的名字。

以下是关于POSIX无名信号量的详细介绍:

  1. :信号量有一个关联的整数值,最初可以设为任意非负整数。
  2. P操作/等待:当线程尝试执行P操作(或称为等待/下降/获取操作)时,如果信号量的值大于0,则它将减少该值并继续。如果值是0,线程将阻塞,直到值变得非零。
  3. V操作/发出:线程执行V操作(或称为发出/增加/释放操作)将增加信号量的值。如果有线程正在因P操作而被阻塞,它们中的一个将被唤醒。

主要函数

  1. 初始化sem_init

    int sem_init(sem_t *sem, int pshared, unsigned int value);
    
    • sem: 指向要初始化的信号量的指针。
    • pshared: 如果为非零值,则信号量在进程间共享。如果为0,则只在进程的线程之间共享。
    • value: 信号量的初始值。
  2. 销毁sem_destroy

    int sem_destroy(sem_t *sem);
    
    • 销毁之前使用sem_init初始化的信号量。
  3. 等待sem_wait

    int sem_wait(sem_t *sem);
    
    • 减少信号量的值。如果值已经是0,那么调用线程将被阻塞,直到信号量的值变为正数。
  4. 尝试等待sem_trywait

    int sem_trywait(sem_t *sem);
    
    • 尝试减少信号量的值。如果值为0,该函数立即返回,不会阻塞。
  5. 发出sem_post

    int sem_post(sem_t *sem);
    
    • 增加信号量的值。如果有线程因sem_wait而被阻塞,其中一个将被唤醒。

使用注意事项

  • 无名信号量主要用于线程间的同步。尽管pshared参数可以使它们在进程之间共享,但通常在这种情况下使用命名信号量更为合适。

  • 当不再需要信号量时,应使用sem_destroy销毁它,以释放相关的资源。

  • 和所有同步原语一样,使用信号量时需要注意避免死锁、活锁和竞争条件。

无名信号量在多线程应用中非常有用,因为它们为线程提供了一种简单且有效的同步机制,特别是当需要对资源访问进行互斥或需要线程之间的协作时。

示例

我们来看一个简单的无名信号量的例子。这个例子中,有两个线程:一个生产者线程和一个消费者线程。生产者生成一个整数并将其存储在共享变量中,消费者读取这个整数。这两个线程通过无名信号量来同步,以确保生产者在消费者读取整数之前生产整数,消费者在生产者生产新的整数之前读取整数。

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem_prod, sem_cons;
int shared_var;

void *producer(void *arg) {
    
    
    for (int i = 1; i <= 5; i++) {
    
    
        sem_wait(&sem_prod); // Wait until the consumer has consumed the last item
        shared_var = i; // Produce an item
        printf("Produced: %d\n", shared_var);
        sem_post(&sem_cons); // Signal the consumer that an item has been produced
    }
    return NULL;
}

void *consumer(void *arg) {
    
    
    for (int i = 1; i <= 5; i++) {
    
    
        sem_wait(&sem_cons); // Wait until the producer has produced an item
        printf("Consumed: %d\n", shared_var);
        sem_post(&sem_prod); // Signal the producer that the item has been consumed
    }
    return NULL;
}

int main() {
    
    
    pthread_t prod_tid, cons_tid;

    sem_init(&sem_prod, 0, 1); // Initialize the producer semaphore to 1
    sem_init(&sem_cons, 0, 0); // Initialize the consumer semaphore to 0

    pthread_create(&prod_tid, NULL, producer, NULL); // Create producer thread
    pthread_create(&cons_tid, NULL, consumer, NULL); // Create consumer thread

    pthread_join(prod_tid, NULL); // Wait for producer thread to finish
    pthread_join(cons_tid, NULL); // Wait for consumer thread to finish

    sem_destroy(&sem_prod); // Destroy the producer semaphore
    sem_destroy(&sem_cons); // Destroy the consumer semaphore

    return 0;
}

执行结果如下:

majn@tiger:~/C_Project/prod_cons$ ./prod_cons 
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
Produced: 3
Consumed: 3
Produced: 4
Consumed: 4
Produced: 5
Consumed: 5

在这个例子中:

  • sem_prodsem_cons 是无名信号量,用于生产者和消费者之间的同步。
  • sem_prod 初始化为1,允许生产者线程首先运行。
  • sem_cons 初始化为0,因为一开始没有可供消费的项目。
  • 生产者线程生成一个整数并将其存储在 shared_var 中,然后通过 sem_post(&sem_cons) 信号消费者可以消费。
  • 消费者线程等待 sem_cons 信号量,读取 shared_var 中的整数,然后通过 sem_post(&sem_prod) 信号生产者可以生产。
  • 主函数等待这两个线程完成,并最后销毁信号量。

】在上述示例中,pthread_t tid; 是一个变量,用于存储新创建的线程的线程ID。此变量在声明时确实没有初始化,但这不是问题,因为它将在 pthread_create 调用中被明确地设置。

当调用 pthread_create 时,其第一个参数是一个指向 pthread_t 类型变量的指针。这个变量用于存储新创建的线程的ID。pthread_create 在成功地创建线程后将会设置它。


pthread_create & pthread_join

pthread_createpthread_join 是 POSIX 线程(通常称为 pthreads)编程中的两个基本函数。它们分别用于创建新的线程和等待线程完成。以下是对这两个函数的详细说明:

1. pthread_create

此函数用于创建一个新的线程。

原型:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

参数:

  • thread: 指向 pthread_t 类型变量的指针,该变量用于存储新线程的ID。
  • attr: 指向 pthread_attr_t 的指针,用于设置线程属性。如果设置为 NULL,则使用默认属性。
  • start_routine: 新线程启动时要执行的函数。这个函数必须返回 void * 并接受一个 void * 参数。
  • arg: 传递给 start_routine 的参数。

返回值:

  • 0: 成功。
  • 错误号: 失败。

2. pthread_join

此函数用于等待线程完成。调用线程会被阻塞,直到指定的线程完成为止。

原型:

int pthread_join(pthread_t thread, void **retval);

参数:

  • thread: 要等待的线程的ID。
  • retval: 一个指向 void * 的指针,用于捕获线程的返回值。如果不关心返回值,可以设置为 NULL

返回值:

  • 0: 成功。
  • 错误号: 失败。

示例

以下是一个简单的示例,说明如何使用 pthread_createpthread_join

#include <stdio.h>
#include <pthread.h>

void *print_hello(void *data) {
    
    
    char *message = (char *)data;
    printf("%s\n", message);
    return NULL;
}

int main() {
    
    
    pthread_t tid;
    char *message = "Hello from the thread!";

    // 创建一个新线程
    if (pthread_create(&tid, NULL, print_hello, message) != 0) {
    
    
        printf("Error creating thread.\n");
        return 1;
    }

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

    printf("Thread has finished execution.\n");
    return 0;
}

在上述示例中,主函数创建了一个新的线程,该线程调用 print_hello 函数并传递一个消息作为参数。然后,主函数使用 pthread_join 等待线程完成执行。

majn@tiger:~/C_Project/pthread_project$ ./pthread_demo 
Hello from the thread!
Thread has finished execution.

猜你喜欢

转载自blog.csdn.net/weixin_43844521/article/details/133240795
今日推荐