C语言-线程

一,线程的概念

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 -eLfUID        PID  PPID   LWP  C NLWP STIME TTY          TIME CMDroot         1     0     1  0    1 Aug07?        00:00:01 /sbin/init splashroot         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 5void* 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 5int 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 5int 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.rhkb.cn/news/433464.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架。它是 Spring 项目家族的一员,用于构建安全的 Java 应用程序。

Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架。它是 Spring 项目家族的一员&#xff0c;用于构建安全的 Java 应用程序。Spring Security 提供了全面的安全服务&#xff0c;从基本的登录认证到复杂的访问控制&#xff0c;几乎涵盖了所有与安全相关的需求…

Paxos 协议详解:分布式系统一致性的基石

文章目录 1. 分布式系统与一致性问题1.1 分布式系统的定义1.2 一致性问题的起源1.3 CAP 定理及其影响1.4 分布式系统中的失败假设 2. Paxos 协议的背景与介绍2.1 Paxos 协议是什么2.3 Paxos 解决什么问题 3. Paxos 的基本原理3.1 Paxos 角色3.2 Paxos 的多数原则3.3 Paxos 协议…

音视频入门基础:FLV专题(1)——FLV官方文档下载

一、FLV简介 Flash Video&#xff08;简称FLV&#xff09;&#xff0c;是一种网络视频格式&#xff0c;用作流媒体格式&#xff0c;它的出现有效地解决了视频文件导入Flash后&#xff0c;使导出的SWF文件体积庞大&#xff0c;不能在网络上有效使用等缺点。 一般FLV文件包在SW…

js删除emoji表情问题

emoji标签占位两个 &#xff0c;直接删除后一位会出现乱码符&#xff1b; 判断是否是emoji function isEmoji(char) {let code char.charCodeAt(0);return code>55296&&code<57343 } // 使用方法&#xff0c;传入单字符 console.log(isEmoji(1)); // false con…

手把手教你找到海外网红合作:海外红人营销渠道

在全球范围内&#xff0c;许多企业寻求与知名网红建立合作关系&#xff0c;以推广产品、共同创作内容或探索其他合作形式。以下是一些有效的方法来实现这一目标&#xff1a; 利用社交媒体平台&#xff1a;社交媒体是寻找海外网红的首选途径。平台如Instagram、YouTube和TikTok拥…

2025届 深圳 嵌入式岗 秋招上岸记录

文章目录 1 背景2 准备阶段2.1 前期2.1.1 掌握的技术栈2.1.2 项目经历2.1.3 比赛&奖学金经历 2.2 中期2.2.1 简历准备2.2.2 个人信息准备2.2.3 企业以及岗位信息的收集2.2.4 个人资料的准备 2.3 简历投递2.3.1 网申2.3.2 招聘会现场投递 3. 简历投递后3.1 测评3.2 笔试3.3 …

基于quill2.0的富文本编辑器,Fluent Editor,支持表格,图片,表情等

官网&#xff1a;Fluent Editor | 基于 Quill 2.0 的富文本编辑器 安装 npm i opentiny/fluent-editor quill 使用案例 <template><div class"publish-form-container"><!-- TODO --><div ref"quillEditorRef" class"quill…

【LLM多模态】视频理解模型Cogvlm-video和MVBench评测基准

note Cogvlm-video模型通过视频抽帧&#xff08;24帧&#xff0c;每帧大小为224 x 224&#xff09;后经过ViT进行图像编码&#xff08;ViT中添加了2x2的卷积核更好的压缩视觉信息&#xff09;&#xff0c;使用adapter模块更好的将视觉特征和文本特征对齐&#xff0c;得到的图像…

ElasticSearch分页查询性能及封装实现

Es的分页方式 fromsize 最基本的分页方式&#xff0c;类似于SQL中的Limit语法&#xff1a; //查询年龄在12到32之间的前15条数据 {"query":{"bool":{"must":{"range":{"user_age":{"gte":12,"lte":3…

基于飞腾平台的OpenCV的编译与安装

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

量化交易backtrader实践(三)_指标与策略篇(1)_指标简介与手工双均线策略

01_指标 指标是一个汉语词语&#xff0c;读音是zhǐ biāo&#xff0c;意思是衡量目标的参数&#xff1b;预期中打算达到的指数、规格、标准&#xff0c;一般用数据表示。 统计学术语 指标是说明总体数量特征的概念及其数值的综合&#xff0c;故又称为综合指标。在实际的统计…

YOLOv7改进之MAE主干: 超强ConvNeXtV2 升级版结构,当MAE+YOLO卷积高效涨点

目录 1,原理概述 2,代码改进 新增一个convnextv2.py文件,增加以下代码 修改部分 第二步:在yolo.py中加入以下代码 然后在 在yolo.py中配置找到./models/yolo.py文件下里的parse_model函数,将类名加入进去 参考代码 YOLOv7网络配置文件 1,原理概述 原文:https://…

openinstall鸿蒙SDK再升级,功能全面支持HarmonyOS NEXT

万众期待的鸿蒙操作系统HarmonyOS NEXT即将发布&#xff0c;国产自主的全场景智能操作系统诞生&#xff0c;将为生态伙伴共创共享创造新蓝海&#xff0c;鸿蒙生态的加速构建&#xff0c;也有望催生出互联网生态的第三极。 作为首批鸿蒙生态伙伴&#xff0c;openinstall在App渠…

这些主流的财务管理软件,你用过哪款?

在当今的商业环境中&#xff0c;财务管理面临着诸多棘手的痛点问题&#xff1a; 数据的准确性与及时性难以保证&#xff0c;手工录入易出错且数据更新常不及时&#xff1b; 预算管理困难重重&#xff0c;编制不合理且执行监控难&#xff1b; 财务风险管控不足&#xff0c;应…

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…

k8s中,pod生命周期,初始化容器,容器探针,事件处理函数,理解其设计思路及作用

k8s中&#xff0c;为什么要设计pod 平台直接管理容器不是挺好的吗 为什么要以pod为单位进行管理&#xff0c; 然后把容器放在pod里面 那么有pod和没pod的区别是什么 也就是pod提供了什么作用 这个可以考虑从pod生命周期管理的角度去思考 如图&#xff0c;pod主容器在运行…

无限大薄板的电场

单块无限大薄板两端的电场 单块无限大的薄板&#xff0c;如果上面带有均匀分布的电荷&#xff0c;就会在薄板的两侧产生电场&#xff0c;电场大小与距离平板的位置无关&#xff0c;方向与平板垂直&#xff0c;如果平板带正电荷&#xff0c;则电场方向向外指向两侧&#xff0c;…

【性能优化】低配starRocks常驻内存优化

背景说明 由于服务器的实际资源小于starRocks官方的配置&#xff0c;导致starRocks在无任务的情况下&#xff0c;常驻内存偏高&#xff0c;可用于查询的资源变小。 官方文档 实际部署的集群一般是4C8G和8C16G&#xff0c;be的配置不达标 为了解决单次查询内存不足的问题&…

史上最详细论文word排版格式指导保姆级教学!

一、前言 首先&#xff0c;每个学校的论文排版格式都是不太相同的&#xff0c;但大体上都是相似的。 正常来说&#xff0c;论文的排版操作是十分枯燥并且重复的&#xff0c;但是word中的样式工具使得论文排版会变得容易。 接下来我将以某个学校论文格式要求为例&#xff0c;…

python socket编程

socket socket&#xff08;套接字&#xff09;是进程间通信的工具&#xff0c;好比现实生活中的插座&#xff0c;所有家电想要工作都是基于插座进行&#xff0c;进程之间想要进行网络通信需要socket socket负责进程之间的网络数据传输&#xff0c;好比数据的搬运工 2个进程之…