当前位置: 首页 > news >正文

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. 死锁预防:在使用条件变量和互斥锁时,要注意避免死锁的发生。例如,确保在等待条件变量之前已经获取了互斥锁,并且在通知条件变量后及时释放互斥锁。

 

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

相关文章:

  • 李宏毅机器学习2022-HW8-Anomaly Detection
  • java的匿名内部类和lambda表达式
  • C语言VS实用调试技巧
  • 隧道灯光远程控制系统的设计与实现(论文+源码)_kaic
  • Spring Boot 进阶-Spring Boot 开发第一个Web接口
  • Java集合框架与Lambda表达式实践指南(小白速看)
  • 大模型Agent开发框架对比:LangGraph/LlamaIndex/DIY
  • 7-1.Android SQLite 之 SQLiteDatabase 简单编码模板(SQLiteDatabase 使用、SQL 语句编写)
  • 工业缺陷检测——Windows 10本地部署AnomalyGPT工业缺陷检测大模型
  • 综合题第二题(路由器的配置)
  • 高考技术——pandas使用
  • FastGPT大模型介绍
  • Python面向对象基础:类属性
  • Vscode把全部‘def‘都收起来的快捷键
  • Mybatis中 #{} 和 ${} 的区别是什么?
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • 30天自制操作系统-2
  • Android系统模拟器绘制实现概述
  • Effective Java 笔记(一)
  • golang 发送GET和POST示例
  • iOS编译提示和导航提示
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • Objective-C 中关联引用的概念
  • PHP 的 SAPI 是个什么东西
  • vagrant 添加本地 box 安装 laravel homestead
  • 百度小程序遇到的问题
  • 成为一名优秀的Developer的书单
  • 对象引论
  • 译自由幺半群
  • 1.Ext JS 建立web开发工程
  • 进程与线程(三)——进程/线程间通信
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • ​Python 3 新特性:类型注解
  • ‌‌雅诗兰黛、‌‌兰蔻等美妆大品牌的营销策略是什么?
  • # 职场生活之道:善于团结
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (SERIES10)DM逻辑备份还原
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (四)库存超卖案例实战——优化redis分布式锁
  • (算法)大数的进制转换
  • (算法设计与分析)第一章算法概述-习题
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)德国人的记事本
  • (转)负载均衡,回话保持,cookie
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .apk 成为历史!
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查