C语言-线程

news2024/9/27 23:55:12

一,线程的概念

1,线程的定义

在 C 语言中,线程是程序执行的最小单位,它是进程中的一个实体,是被系统独立调度和分派的基本单位。

2、线程的特点

  1. 轻型实体:线程是一个轻型实体,它只拥有必不可少的资源,如程序计数器、一组寄存器和栈等。与进程相比,线程的创建、切换和销毁的开销要小得多。
  2. 独立调度:线程是独立调度的基本单位,在多线程操作系统中,线程的调度不需要经过进程切换,因此可以提高系统的并发性和响应速度。
  3. 共享进程资源:同一进程中的多个线程可以共享该进程的资源,如代码段、数据段、文件描述符等。这使得线程之间的通信和数据共享更加方便和高效。
  4. 并发性:多个线程可以在同一时间内并发执行,从而提高程序的执行效率。

3、线程的组成部分

  1. 线程 ID:每个线程都有一个唯一的线程 ID,用于标识该线程。
  2. 线程状态:线程的状态包括就绪状态、运行状态、阻塞状态等。线程的状态会随着程序的执行而不断变化。
  3. 线程的栈:线程有自己的栈空间,用于存储局部变量、函数调用参数和返回地址等。
  4. 线程的寄存器:线程有自己的一组寄存器,用于存储当前线程的执行状态。
  5. 线程的优先级:线程可以有不同的优先级,优先级高的线程会优先获得 CPU 时间片。

4、线程与进程的区别

  1. 调度单位:进程是资源分配的基本单位,线程是独立调度的基本单位。
  2. 并发性:进程之间的并发性较低,因为进程切换的开销较大;线程之间的并发性较高,因为线程切换的开销较小。
  3. 拥有资源:进程拥有独立的地址空间和资源,线程共享进程的地址空间和资源。
  4. 通信方式:进程之间的通信需要通过操作系统提供的机制,如管道、消息队列、共享内存等;线程之间的通信可以直接通过共享内存和变量进行,更加方便和高效。

联系紧密的任务在并发时优先选择多线程,如果任务之间比较独立,在并发时建议选择多进程。

4、线程的应用场景

  1. 多任务处理:在需要同时执行多个任务的程序中,可以使用线程来提高程序的并发性和响应速度。例如,在图形用户界面程序中,可以使用线程来处理用户输入、更新界面和执行后台任务等。
  2. 并行计算:在需要进行并行计算的程序中,可以使用线程来充分利用多核处理器的性能。例如,在科学计算、图像处理和数据挖掘等领域,可以使用线程来加速计算过程。
  3. 网络编程:在网络编程中,可以使用线程来处理多个客户端的连接请求。例如,在服务器程序中,可以使用线程来为每个客户端连接创建一个独立的线程,从而提高服务器的并发处理能力。

总之,线程是 C 语言中一种重要的编程概念,它可以提高程序的并发性和响应速度,方便地实现多任务处理和并行计算等应用场景。

二,线程的相命令

Linux 系统有很多命令可以查看进程,例如 pidstat top ps ,也可以查看一个进程下的线程

一、查看线程信息

 
  1. ps命令:
    • ps -eLf可以显示所有进程的详细信息,包括线程信息。每一行代表一个线程,其中可以看到线程 ID(LWP 列)、所属进程 ID(PID 列)等信息。

 

     $ ps -eLf
     UID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
     root         1     0     1  0    1 Aug07?        00:00:01 /sbin/init splash
     root         2     0     2  0    1 Aug07?        00:00:00 [kthreadd]
    ...
 
  1. top命令:
    • top命令的交互界面中,按H键可以切换到显示线程模式。可以看到每个进程下的线程及其 CPU 使用率等信息。
 

二、调试工具

 
  1. gdb
    • gdb是一个强大的调试工具,可以用于调试多线程程序。可以使用info threads命令查看当前被调试程序中的所有线程,使用thread <thread-id>命令切换到特定的线程进行调试。
 

三、线程特定的库函数调用(在 C 程序中)

 
  1. 使用 POSIX 线程库(pthreads)函数,如前面提到的pthread_createpthread_joinpthread_exit等,可以在程序中管理线程。这些函数不是命令行工具,但在编写和调试多线程程序时非常重要。
 

例如,以下是一个使用pthread_create创建线程的 C 程序:

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

void *thread_function(void *arg) {
    printf("This is a thread.\n");
    return NULL;
}

int main() {
    pthread_t thread_id;
    pthread_create(&thread_id, NULL, thread_function, NULL);
    pthread_join(thread_id, NULL);
    return 0;
}
 

编译并运行这个程序后,可以使用上述提到的pstop等命令查看线程的运行情况。

三,线程的基本使用

在 C 语言中,使用 POSIX 线程库(pthreads)可以进行线程的创建、等待、退出和分离操作。以下是对这些操作的详细介绍:

一、线程的创建

使用pthread_create函数可以创建一个新线程。

函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

参数说明:

  • thread:指向线程 ID 的指针,用于存储新创建线程的标识符。
  • attr:线程属性指针,可以设置线程的属性,如优先级、栈大小等。如果为NULL,则使用默认属性。
  • start_routine:指向线程函数的指针,新线程将从这个函数开始执行。
  • arg:传递给线程函数的参数。

例如:

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

void* threadFunction(void* arg) {
    int* value = (int*)arg;
    printf("Thread started with value: %d\n", *value);
    return NULL;
}

int main() {
    pthread_t thread_id;
    int argument = 42;
    int result = pthread_create(&thread_id, NULL, threadFunction, &argument);
    if (result!= 0) {
        perror("pthread_create");
        return 1;
    }
    return 0;
}

二、线程的等待

使用pthread_join函数可以等待一个线程结束。

函数原型:int pthread_join(pthread_t thread, void **retval);

参数说明:

  • thread:要等待的线程的标识符。
  • retval:用于接收线程的返回值,如果不需要可以设置为NULL

例如:

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

void* threadFunction(void* arg) {
    printf("Thread is running.\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t thread_id;
    int result = pthread_create(&thread_id, NULL, threadFunction, NULL);
    if (result!= 0) {
        perror("pthread_create");
        return 1;
    }
    void* threadReturnValue;
    result = pthread_join(thread_id, &threadReturnValue);
    if (result!= 0) {
        perror("pthread_join");
        return 1;
    }
    printf("Thread has ended.\n");
    return 0;
}

三、线程的退出

线程可以通过以下几种方式退出:

  1. 从线程函数中返回:当线程函数执行完毕并返回时,线程自然退出。
  2. 使用pthread_exit函数显式退出:

函数原型:void pthread_exit(void *retval);

参数说明:retval是线程的返回值,可以被其他等待该线程的线程获取。

例如:

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

void* threadFunction(void* arg) {
    printf("Thread is about to exit.\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t thread_id;
    int result = pthread_create(&thread_id, NULL, threadFunction, NULL);
    if (result!= 0) {
        perror("pthread_create");
        return 1;
    }
    printf("Main thread continues.\n");
    return 0;
}

四、线程的分离

使用pthread_detach函数可以将一个线程设置为分离状态,这样当线程结束时,其资源可以被自动回收,而不需要其他线程调用pthread_join来等待它。

函数原型:int pthread_detach(pthread_t thread);

例如:

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

void* threadFunction(void* arg) {
    printf("Thread is running in detached state.\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t thread_id;
    int result = pthread_create(&thread_id, NULL, threadFunction, NULL);
    if (result!= 0) {
        perror("pthread_create");
        return 1;
    }
    result = pthread_detach(thread_id);
    if (result!= 0) {
        perror("pthread_detach");
        return 1;
    }
    printf("Main thread continues without waiting for the detached thread.\n");
    return 0;
}

在使用线程时,需要根据具体的需求选择合适的方式来管理线程的生命周期,以确保程序的正确性和稳定性。

四,创建多个线程

创建多个线程时,一般由主线程统一创建,并等待释放资源或者分离线程 , 不要递归创建
        1. 多个线程如果任务相同,则可以使用同一个线程执行函数
        2. 多个线程如果任务不同,则可以使用不同的线程执行函数

在 C 语言中,可以使用 pthreads来创建多个线程。以下是一个示例代码:

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

#define NUM_THREADS 5

void* printThreadNumber(void* threadId) {
    long tid = (long)threadId;
    printf("This is thread %ld\n", tid);
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    int rc;
    long t;
    for (t = 0; t < NUM_THREADS; t++) {
        printf("In main: creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, printThreadNumber, (void*)t);
        if (rc) {
            perror("pthread_create");
            return -1;
        }
    }

    // 等待所有线程完成
    for (t = 0; t < NUM_THREADS; t++) {
        rc = pthread_join(threads[t], NULL);
        if (rc) {
            perror("pthread_join");
            return -1;
        }
    }

    printf("All threads completed.\n");
    pthread_exit(NULL);
}

在这个例子中,首先定义了一个线程函数printThreadNumber,它接受一个线程 ID 作为参数,并打印出该线程的编号。在main函数中,创建了NUM_THREADS个线程,每个线程都调用printThreadNumber函数。然后,使用pthread_join等待所有线程完成。

注意,在实际应用中,需要根据具体需求对线程函数进行修改,并合理处理线程的同步和资源管理等问题。

五,线程间的通信

一、共享变量

  1. 最直接的方式是通过共享变量进行通信。多个线程可以访问和修改同一个共享变量。
  2. 但这种方式需要注意同步问题,以避免竞争条件和数据不一致。可以使用互斥锁(mutex)或条件变量(condition variable)来确保线程安全地访问共享变量。

例如:

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

int sharedVariable = 0;
pthread_mutex_t mutex;

void* incrementThread(void* arg) {
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&mutex);
        sharedVariable++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void* printThread(void* arg) {
    while (1) {
        pthread_mutex_lock(&mutex);
        if (sharedVariable > 5) {
            printf("Shared variable is greater than 5: %d\n", sharedVariable);
            pthread_mutex_unlock(&mutex);
            break;
        } else {
            pthread_mutex_unlock(&mutex);
        }
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&thread1, NULL, incrementThread, NULL);
    pthread_create(&thread2, NULL, printThread, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_mutex_destroy(&mutex);
    return 0;
}

在这个例子中,两个线程通过共享变量sharedVariable进行通信。一个线程不断增加共享变量的值,另一个线程在共享变量大于 5 时打印其值。使用互斥锁来确保对共享变量的安全访问。

二、条件变量

  1. 条件变量允许一个线程等待特定的条件满足,而另一个线程可以通知条件已满足。
  2. 通常与互斥锁一起使用,以确保线程安全地访问共享资源和等待条件。

例如:

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

int sharedVariable = 0;
pthread_mutex_t mutex;
pthread_cond_t conditionVariable;

void* producerThread(void* arg) {
    for (int i = 0; i < 5; i++) {
        pthread_mutex_lock(&mutex);
        sharedVariable++;
        pthread_cond_signal(&conditionVariable);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    return NULL;
}

void* consumerThread(void* arg) {
    pthread_mutex_lock(&mutex);
    while (sharedVariable == 0) {
        pthread_cond_wait(&conditionVariable, &mutex);
    }
    printf("Consumed: %d\n", sharedVariable);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t producer, consumer;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&conditionVariable, NULL);
    pthread_create(&producer, NULL, producerThread, NULL);
    pthread_create(&consumer, NULL, consumerThread, NULL);
    pthread_join(producer, NULL);
    pthread_join(consumer, NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&conditionVariable);
    return 0;
}

在这个例子中,生产者线程不断增加共享变量的值,并在每次增加后通知条件变量。消费者线程等待共享变量不为零的条件,当条件满足时,打印共享变量的值。

三、信号量

  1. 信号量可以用于控制对共享资源的访问,实现线程间的同步和互斥。
  2. 可以使用 POSIX 信号量或自定义的信号量实现来进行线程间通信。

例如:

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

int sharedVariable = 0;
sem_t semaphore;

void* incrementThread(void* arg) {
    for (int i = 0; i < 10; i++) {
        sharedVariable++;
        sem_post(&semaphore);
    }
    return NULL;
}

void* printThread(void* arg) {
    while (1) {
        sem_wait(&semaphore);
        if (sharedVariable > 5) {
            printf("Shared variable is greater than 5: %d\n", sharedVariable);
            break;
        }
    }
    return NULL;
}

int main() {
    sem_init(&semaphore, 0, 0);
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, incrementThread, NULL);
    pthread_create(&thread2, NULL, printThread, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    sem_destroy(&semaphore);
    return 0;
}

在这个例子中,使用信号量来控制对共享变量的访问。一个线程增加共享变量的值并释放信号量,另一个线程等待信号量并在共享变量大于 5 时打印其值。

六,线程互斥锁

在 C 语言中,使用 POSIX 线程库(pthreads)可以实现线程互斥锁来保护共享资源,避免多个线程同时访问时出现数据不一致或竞争条件的问题。

一、互斥锁的概念

互斥锁(mutex)是一种用于线程同步的机制,它确保在任何时候只有一个线程可以访问被互斥锁保护的资源。当一个线程获取互斥锁时,其他线程如果试图获取该锁,将被阻塞,直到持有锁的线程释放锁。

二、互斥锁的使用步骤

  1. 包含头文件:

    • 在使用互斥锁之前,需要包含<pthread.h>头文件。
  2. 声明互斥锁变量:

    • 使用pthread_mutex_t类型声明一个互斥锁变量。
  3. 初始化互斥锁:

    • 在使用互斥锁之前,需要使用pthread_mutex_init函数对其进行初始化。
    • 函数原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
    • 参数说明:
      • mutex是指向互斥锁变量的指针。
      • attr是互斥锁的属性指针,可以设置为NULL,使用默认属性。
  4. 加锁和解锁互斥锁:

    • 使用pthread_mutex_lock函数对互斥锁进行加锁,确保只有一个线程可以访问被保护的资源。
    • 使用pthread_mutex_unlock函数对互斥锁进行解锁,允许其他线程获取锁并访问资源。
    • 函数原型:
      • int pthread_mutex_lock(pthread_mutex_t *mutex);
      • int pthread_mutex_unlock(pthread_mutex_t *mutex);
    • 参数说明:mutex是指向互斥锁变量的指针。
  5. 销毁互斥锁:

    • 在不再需要互斥锁时,使用pthread_mutex_destroy函数销毁互斥锁。
    • 函数原型:int pthread_mutex_destroy(pthread_mutex_t *mutex);
    • 参数说明:mutex是指向互斥锁变量的指针。

三、示例代码

以下是一个使用互斥锁保护共享资源的示例代码:

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

int sharedVariable = 0;
pthread_mutex_t mutex;

void* incrementThread(void* arg) {
    for (int i = 0; i < 1000; i++) {
        pthread_mutex_lock(&mutex);
        sharedVariable++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    pthread_mutex_init(&mutex, NULL);
    pthread_create(&thread1, NULL, incrementThread, NULL);
    pthread_create(&thread2, NULL, incrementThread, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_mutex_destroy(&mutex);
    printf("Shared variable value: %d\n", sharedVariable);
    return 0;
}

在这个例子中,两个线程同时对共享变量sharedVariable进行递增操作。使用互斥锁来确保在任何时候只有一个线程可以访问共享变量,从而避免了竞争条件。

四、注意事项

  1. 死锁问题:如果多个线程以不同的顺序获取多个互斥锁,可能会导致死锁。在设计程序时,要注意避免死锁的发生。
  2. 错误处理:在使用互斥锁的函数时,要检查返回值并进行适当的错误处理,以确保程序的稳定性。
  3. 可重入性:互斥锁应该是可重入的,即同一个线程可以多次获取同一个互斥锁而不会导致死锁。

总之,在 C 语言中,使用互斥锁可以有效地保护共享资源,避免线程之间的竞争条件。在使用互斥锁时,要注意正确地初始化、加锁、解锁和销毁互斥锁,并避免死锁和错误处理问题。

七,线程同步

一、线程同步的概念

        线程同步:是指在互斥的基础上,通过其它机制实现访问者对 资源的有序访问
        条件变量:线程库提供的专门针对线程同步的机制
        线程同步比较典型的应用场合就是 生产者与消费者

二、生产者与消费者模型原理

1. 在这个模型中,包括 生产者线程 消费者线程 。通过线程来模拟多个线程同步的过程
2. 在这个模型中 , 需要以下组件
        仓库 : 用于存储产品,一般作为共享资源
        生产者线程 : 用于生产产品
        消费者线程 : 用于消费产品
3. 原理
        当仓库没有产品时,则消费者线程需要等待,直到有产品时才能消费
        当仓库已经装满产品时,则生产者线程需要等待,直到消费者线程消费产品之后

三,示例代码

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

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE];
int count = 0;
int in = 0;
int out = 0;

pthread_mutex_t mutex;
pthread_cond_t full;
pthread_cond_t empty;

void* producer(void* arg) {
    int item;
    while (1) {
        item = rand() % 100;
        pthread_mutex_lock(&mutex);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&full, &mutex);
        }
        buffer[in] = item;
        in = (in + 1) % BUFFER_SIZE;
        count++;
        pthread_cond_signal(&empty);
        pthread_mutex_unlock(&mutex);
        printf("Produced item: %d\n", item);
        usleep(500000); // 模拟生产时间
    }
    return NULL;
}

void* consumer(void* arg) {
    int item;
    while (1) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&empty, &mutex);
        }
        item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        pthread_cond_signal(&full);
        pthread_mutex_unlock(&mutex);
        printf("Consumed item: %d\n", item);
        usleep(800000); // 模拟消费时间
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&full, NULL);
    pthread_cond_init(&empty, NULL);

    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&full);
    pthread_cond_destroy(&empty);

    return 0;
}

在这个示例中:

 

1、共享资源和状态变量

 
  • buffer是一个整数数组,作为生产者和消费者共享的缓冲区。
  • count表示缓冲区中当前的物品数量。
  • inout分别是生产者放入物品和消费者取出物品的索引。
 

2、线程同步机制

 
  1. 互斥锁(pthread_mutex_t mutex:确保在任何时候只有一个线程可以访问共享资源(缓冲区和状态变量)。
 

3、生产者线程(producer函数)

 
  • 不断生成随机数作为要生产的物品。
  • 使用互斥锁锁定共享资源,当缓冲区已满(count == BUFFER_SIZE)时,等待条件变量full
  • 将物品放入缓冲区,更新索引和物品数量,然后通过信号量通知消费者线程(pthread_cond_signal(&empty))。
 

4、消费者线程(consumer函数)

 
  • 不断尝试从缓冲区中取出物品进行消费。
  • 使用互斥锁锁定共享资源,当缓冲区为空(count == 0)时,等待条件变量empty
  • 从缓冲区中取出物品,更新索引和物品数量,然后通过信号量通知生产者线程(pthread_cond_signal(&full))。
 

5、注意事项

 
  1. 死锁预防:确保在等待条件变量之前已经持有互斥锁,并且在唤醒其他线程后及时释放互斥锁,以避免死锁的发生。
  2. 正确的条件判断:在等待条件变量之前,要确保条件判断的准确性。例如,在这个模型中,生产者线程在判断缓冲区已满时,应该使用while循环而不是if语句,以防止虚假唤醒。
  3. 可扩展性:可以根据实际需求扩展这个模型,例如增加多个生产者和消费者线程,或者修改缓冲区的大小和数据类型。

八,条件变量

简介:

上节示例代码的不足:
        主线程( 消费者线程 ) 需要不断查询是否有产品可以消费,如果没有产品可以消费,也在运行程序,包括获 得互斥锁、判断条件、释放互斥锁,非常消耗 cpu 资源。 使用条件变量进行修改
        条件变量 允许一个线程就某个共享变量的状态变化通知其他线程,并让其他线程等待这一通知
基于条件变量的阻塞与唤醒,具体的原理如下图
       

        step 1 :消费者线程判断消费条件是否满足 ( 仓库是否有产品 ) ,如果有产品则可以消费,然后解锁
        step 2 :当条件满足时 ( 仓库产品数量为 0) ,则调用 pthread_cond_wait 函数 , 这个函数具体做的事 情如下 :
        在线程睡眠之前,对互斥锁解锁
        让线程进入到睡眠状态
        等待条件变量收到信号时,该函数重新竞争锁,并获取锁
        step 3 :重新判断条件是否满足,如果满足,则继续调用 pthread_cond_wait 函数
        step 4 :唤醒后,从 pthread_cond_wait 返回,条件不满足,则正常消费产品
        step 5 :释放锁,整个过程结束

一、条件变量的概念

 
  1. 条件变量的作用:

    • 条件变量用于解决线程之间的等待和通知问题。当一个线程需要等待某个条件满足时,它可以使用条件变量进行等待。当另一个线程使条件满足时,它可以通知等待的线程继续执行。
    • 条件变量通常与互斥锁一起使用,以确保对共享资源的安全访问和正确的等待和通知操作。
  2. 条件变量的组成:

    • 条件变量由一个等待队列和一个信号机制组成。当一个线程调用pthread_cond_wait函数等待条件变量时,它会被放入等待队列中,并释放互斥锁。当另一个线程调用pthread_cond_signalpthread_cond_broadcast函数通知条件变量时,等待队列中的一个或多个线程会被唤醒,并重新获取互斥锁。

二、条件变量的使用步骤

 
  1. 包含头文件:

    • 在使用条件变量之前,需要包含<pthread.h>头文件。
  2. 声明条件变量和互斥锁变量:

    • 使用pthread_cond_t类型声明一个条件变量变量,使用pthread_mutex_t类型声明一个互斥锁变量。
  3. 初始化条件变量和互斥锁:

    • 在使用条件变量和互斥锁之前,需要使用pthread_cond_initpthread_mutex_init函数对它们进行初始化。
    • 函数原型:
      • int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
      • int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
    • 参数说明:
      • cond是指向条件变量变量的指针。
      • attr是条件变量或互斥锁的属性指针,可以设置为NULL,使用默认属性。
  4. 使用条件变量和互斥锁:

    • 在需要等待条件变量的线程中,首先获取互斥锁,然后检查条件是否满足。如果条件不满足,则调用pthread_cond_wait函数等待条件变量。
    • 在需要通知条件变量的线程中,首先获取互斥锁,然后修改共享资源,使条件满足。最后,调用pthread_cond_signalpthread_cond_broadcast函数通知等待的线程。
    • 函数原型:
      • int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
      • int pthread_cond_signal(pthread_cond_t *cond);
      • int pthread_cond_broadcast(pthread_cond_t *cond);
    • 参数说明:
      • cond是指向条件变量变量的指针。
      • mutex是指向互斥锁变量的指针。
  5. 销毁条件变量和互斥锁:

    • 在不再需要条件变量和互斥锁时,使用pthread_cond_destroypthread_mutex_destroy函数销毁它们。
    • 函数原型:
      • int pthread_cond_destroy(pthread_cond_t *cond);
      • int pthread_mutex_destroy(pthread_mutex_t *mutex);
    • 参数说明:condmutex是指向条件变量变量和互斥锁变量的指针。
 

三、示例代码

 

以下是一个使用条件变量实现生产者 - 消费者模型的示例代码:

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

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE];
int count = 0;
int in = 0;
int out = 0;

pthread_mutex_t mutex;
pthread_cond_t full;
pthread_cond_t empty;

void* producer(void* arg) {
    int item;
    while (1) {
        item = rand() % 100;
        pthread_mutex_lock(&mutex);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&full, &mutex);
        }
        buffer[in] = item;
        in = (in + 1) % BUFFER_SIZE;
        count++;
        pthread_cond_signal(&empty);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void* consumer(void* arg) {
    int item;
    while (1) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&empty, &mutex);
        }
        item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        pthread_cond_signal(&full);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&full, NULL);
    pthread_cond_init(&empty, NULL);

    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&full);
    pthread_cond_destroy(&empty);

    return 0;
}
 

在这个示例中,生产者线程和消费者线程通过条件变量和互斥锁进行同步。生产者线程在生产物品时,如果缓冲区已满,则等待条件变量full。消费者线程在消费物品时,如果缓冲区为空,则等待条件变量empty。当生产者线程生产物品后,会通知消费者线程,即通过pthread_cond_signal(&empty)唤醒等待在条件变量empty上的消费者线程。当消费者线程消费物品后,会通知生产者线程,即通过pthread_cond_signal(&full)唤醒等待在条件变量full上的生产者线程。

 

四、注意事项

 
  1. 正确的等待和通知顺序:在使用条件变量时,要确保等待条件变量的线程在检查条件之前已经获取了互斥锁,并且在等待条件变量时释放了互斥锁。通知条件变量的线程在修改共享资源使条件满足后,再通知等待的线程。
  2. 避免虚假唤醒:在等待条件变量时,可能会出现虚假唤醒的情况,即线程在没有被其他线程通知的情况下被唤醒。为了避免虚假唤醒,通常在等待条件变量时使用while循环而不是if语句来检查条件。
  3. 死锁预防:在使用条件变量和互斥锁时,要注意避免死锁的发生。例如,确保在等待条件变量之前已经获取了互斥锁,并且在通知条件变量后及时释放互斥锁。

 

总之,条件变量是一种强大的线程间同步机制,可以用于解决复杂的同步问题。在使用条件变量时,要注意正确的使用方法和避免常见的错误,以确保程序的正确性和稳定性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2171541.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学生党有福了!分享5个免费的AI论文生成工具

学生党在学术写作方面常常面临时间紧迫和写作能力不足的问题。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;市面上出现了许多免费的AI论文生成工具&#xff0c;极大地提高了写作效率和质量。今天&#xff0c;我将向大家推荐五款免费的AI论文生成工具&#xff0c;并…

再也不用担心内容重复!在线伪原创工具,让创作更自由!

大家好&#xff0c;今天我们将讨论一个对网络写作非常有益的辅助工具——在线内容转换工具。不论您是需要更新您的博客&#xff0c;还是希望在社交平台上保持活跃&#xff0c;我们都频繁面临着迅速生成新内容的挑战。利用一个有效的工具来改写现有内容&#xff0c;可以极大地提…

一次实践:给自己的手机摄像头进行相机标定

文章目录 1. 问题引入2. 准备工作2.1 标定场2.2 相机拍摄 3. 基本原理3.1 成像原理3.2 畸变校正 4. 标定解算4.1 代码实现4.2 详细解析4.2.1 解算实现4.2.2 提取点位 4.3 解算结果 5. 问题补充 1. 问题引入 不得不说&#xff0c;现在的计算机视觉技术已经发展到足够成熟的阶段…

Python画笔案例-067 绘制配乐七角星

1、绘制橙子 通过 python 的turtle 库绘制 配乐七角星,如下图: 2、实现代码 绘制 配乐七角星 ,以下为实现代码: """配乐七角星.py本程序需要coloradd模块支持,安装方法:pip install coloradd""" import turtle from coloradd import color…

制造企业如何提升项目管理效率?惠科股份选择奥博思PowerProject项目管理系统

全球知名的显示方案综合服务商 - 惠科股份有限公司与北京奥博思达成合作&#xff0c;基于奥博思 PowerProject 搭建企业级项目管理平台。满足惠科多产品多业务领域的项目全周期管理。助力企业在技术研发、产品创新等方面继续取得行业领先优势。 同时&#xff0c;PowerProject …

如何进行u盘拷贝文件管控?5个方法一文详情告诉你!

小李&#xff1a;老王&#xff0c;最近我们部门经常提到数据安全的问题&#xff0c;特别是U盘拷贝文件带来的风险。 你有什么好办法可以管控一下吗&#xff1f; 老王&#xff1a;小李啊&#xff0c;你问对人了。 U盘拷贝文件管控确实是个头疼的问题&#xff0c;但我们可以从…

C++入门day5-面向对象编程(终)

C入门day4-面向对象编程&#xff08;下&#xff09;-CSDN博客 本节是我们面向对象内容的最终篇章&#xff0c;不是说我们的C就学到这里。如果有一些面向对象的基础知识没有讲到&#xff0c;后面会发布在知识点补充专栏&#xff0c;全都是干货满满的。 https://blog.csdn.net/u…

【中级通信工程师】终端与业务(九):市场细分与选择

【零基础3天通关中级通信工程师】 终端与业务(九)&#xff1a;市场细分与选择 本文是中级通信工程师考试《终端与业务》科目第九章《市场细分与选择》的复习资料和真题汇总。本章的核心内容涵盖了市场细分的概念与方法、目标市场选择策略以及市场定位的原则和步骤。通过本节的…

​fl studio21.2.3.4004中文版永久2024最新下载安装图文详细使用教程​

随着数字音乐制作的快速发展&#xff0c;越来越多的音乐制作软件涌现出来&#xff0c;而FL Studio无疑是其中的佼佼者。作为一款功能强大、易于上手的音乐制作软件&#xff0c;FL Studio V21中文版在继承了前代版本优秀基因的基础上&#xff0c;进一步提升了用户体验&#xff0…

用于MRI重建的具有全局感受野的傅里叶卷积块|文献速递--基于多模态-半监督深度学习的病理学诊断与病灶分割

Title 题目 Fourier Convolution Block with global receptive field for MRI reconstruction 用于MRI重建的具有全局感受野的傅里叶卷积块 01 文献速递介绍 从欠采样的磁共振成像&#xff08;MRI&#xff09;信号中重建图像可以显著减少扫描时间并改善临床实践。然而&…

rk3588S 调试USB摄像头

问题: 客户的 usb 摄像头 接上 板卡上的 USB2.0 的接口是可以的,但是 接上 typec 接口上的 OTGUSB的时候 ,就会出现,无法识别USB的问题。 情况的说明: 先来看一下硬件。 这里的 typec 接口实际上 只用到了 otg USB的 两根线, 也就是 把TYPEC 当做 USB2.0 来用了。(通…

Cannot read properties of undefined (reading ‘upgrade‘)

前端开发工具&#xff1a;VSCODE 报错信息&#xff1a; INFO Starting development server...10% building 2/2 modules 0 active ERROR TypeError: Cannot read properties of undefined (reading upgrade)TypeError: Cannot read properties of undefined (reading upgrade…

vxe-table制作高亮刷新功能

start 记录一下 vxe-table 实现表格新增数据背景闪烁功能。 1. 效果 2. demo代码 <template><div id"app"><div click"tomato">点我新增数据 lazy_tomato</div><vxe-grid refxTable :height"height" :columns&quo…

原文翻译:Make Skeleton-based Action Recognition Model Smaller, Faster and Better

全网没找到一个完整的翻译&#xff0c;用chatgpt翻译如下&#xff0c;可能有的地方不够准确&#xff0c;推荐结合原文对照着看更。 摘要 尽管基于骨架的动作识别在近年来取得了巨大的成功&#xff0c;但大多数现有方法可能面临模型规模庞大和执行速度缓慢的问题。为了解决这个…

Apifox 9月更新|「动态值」全新升级、跨团队引用接口和测试场景、测试报告交互优化

Apifox 新版本上线啦&#xff01;&#xff01;&#xff01; 看看本次版本更新主要涵盖的重点内容&#xff0c;有没有你所关注的功能特性&#xff1a; 「动态值」全新升级 更强大、更灵活的数据模拟能力 支持智能代码补全动态值 测试报告交互优化 支持跨团队引用接口和测试场…

LLM大模型学习:开源大模型技术路线及趋势

MLNLP社区是国内外知名的机器学习与自然语言处理社区&#xff0c;受众覆盖国内外NLP硕博生、高校老师以及企业研究人员。 社区的愿景是促进国内外自然语言处理&#xff0c;机器学习学术界、产业界和广大爱好者之间的交流和进步&#xff0c;特别是初学者同学们的进步。 转载自…

数电基础(组合逻辑电路+Proteus)

1.组合逻辑电路 1.1组合逻辑电路的分析 1.1.1组合逻辑电路的定义 组合逻辑电路的定义 &#xff08;1&#xff09;对于一个逻辑电路&#xff0c;其输出状态在任何时刻只取决于同一时刻的输入状态&#xff0c;而与电路的原来状态无关&#xff0c;这种电路被定义为组合逻辑电路…

MySQL 之索引详解

想象一下&#xff0c;你正在图书馆寻找一本关于 MySQL 索引的书。图书馆里有成千上万本书&#xff0c;但没有目录。你只能一排一排、一本一本地找&#xff0c;直到找到你想要的书。这将会花费大量的时间&#xff01;数据库索引就像图书馆的目录一样&#xff0c;可以帮助数据库系…

什么是智享AI直播(三代)?一文带你全面解析!

什么是智享AI直播&#xff08;三代&#xff09;&#xff1f;一文带你全面解析&#xff01; 在当今这个数字化飞速发展的时代&#xff0c;技术的每一次革新都深刻地改变着我们的生活与工作方式。随着人工智能&#xff08;AI&#xff09;技术的不断成熟与普及&#xff0c;智享AI…

【mysql】千万级数据MySQL索引优化实例

【mysql】千万级数据MySQL索引优化实例 【一】场景描述【二】生成数千万条记录【三】原始sql分析【四】第一次优化&#xff1a;常规索引【五】第二次优化&#xff1a;覆盖索引【六】第三次优化&#xff1a;减少数据量【七】第四次优化&#xff1a;小表驱动大表【八】第五次优化…