一,线程的概念
1,线程的定义
在 C 语言中,线程是程序执行的最小单位,它是进程中的一个实体,是被系统独立调度和分派的基本单位。
2、线程的特点
- 轻型实体:线程是一个轻型实体,它只拥有必不可少的资源,如程序计数器、一组寄存器和栈等。与进程相比,线程的创建、切换和销毁的开销要小得多。
- 独立调度:线程是独立调度的基本单位,在多线程操作系统中,线程的调度不需要经过进程切换,因此可以提高系统的并发性和响应速度。
- 共享进程资源:同一进程中的多个线程可以共享该进程的资源,如代码段、数据段、文件描述符等。这使得线程之间的通信和数据共享更加方便和高效。
- 并发性:多个线程可以在同一时间内并发执行,从而提高程序的执行效率。
3、线程的组成部分
- 线程 ID:每个线程都有一个唯一的线程 ID,用于标识该线程。
- 线程状态:线程的状态包括就绪状态、运行状态、阻塞状态等。线程的状态会随着程序的执行而不断变化。
- 线程的栈:线程有自己的栈空间,用于存储局部变量、函数调用参数和返回地址等。
- 线程的寄存器:线程有自己的一组寄存器,用于存储当前线程的执行状态。
- 线程的优先级:线程可以有不同的优先级,优先级高的线程会优先获得 CPU 时间片。
4、线程与进程的区别
- 调度单位:进程是资源分配的基本单位,线程是独立调度的基本单位。
- 并发性:进程之间的并发性较低,因为进程切换的开销较大;线程之间的并发性较高,因为线程切换的开销较小。
- 拥有资源:进程拥有独立的地址空间和资源,线程共享进程的地址空间和资源。
- 通信方式:进程之间的通信需要通过操作系统提供的机制,如管道、消息队列、共享内存等;线程之间的通信可以直接通过共享内存和变量进行,更加方便和高效。
4、线程的应用场景
- 多任务处理:在需要同时执行多个任务的程序中,可以使用线程来提高程序的并发性和响应速度。例如,在图形用户界面程序中,可以使用线程来处理用户输入、更新界面和执行后台任务等。
- 并行计算:在需要进行并行计算的程序中,可以使用线程来充分利用多核处理器的性能。例如,在科学计算、图像处理和数据挖掘等领域,可以使用线程来加速计算过程。
- 网络编程:在网络编程中,可以使用线程来处理多个客户端的连接请求。例如,在服务器程序中,可以使用线程来为每个客户端连接创建一个独立的线程,从而提高服务器的并发处理能力。
总之,线程是 C 语言中一种重要的编程概念,它可以提高程序的并发性和响应速度,方便地实现多任务处理和并行计算等应用场景。
二,线程的相命令
一、查看线程信息
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]
...
top
命令:- 在
top
命令的交互界面中,按H
键可以切换到显示线程模式。可以看到每个进程下的线程及其 CPU 使用率等信息。
- 在
二、调试工具
gdb
:gdb
是一个强大的调试工具,可以用于调试多线程程序。可以使用info threads
命令查看当前被调试程序中的所有线程,使用thread <thread-id>
命令切换到特定的线程进行调试。
三、线程特定的库函数调用(在 C 程序中)
- 使用 POSIX 线程库(pthreads)函数,如前面提到的
pthread_create
、pthread_join
、pthread_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;
}
编译并运行这个程序后,可以使用上述提到的ps
和top
等命令查看线程的运行情况。
三,线程的基本使用
在 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;
}
三、线程的退出
线程可以通过以下几种方式退出:
- 从线程函数中返回:当线程函数执行完毕并返回时,线程自然退出。
- 使用
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;
}
在使用线程时,需要根据具体的需求选择合适的方式来管理线程的生命周期,以确保程序的正确性和稳定性。
四,创建多个线程
在 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
等待所有线程完成。
注意,在实际应用中,需要根据具体需求对线程函数进行修改,并合理处理线程的同步和资源管理等问题。
五,线程间的通信
一、共享变量
- 最直接的方式是通过共享变量进行通信。多个线程可以访问和修改同一个共享变量。
- 但这种方式需要注意同步问题,以避免竞争条件和数据不一致。可以使用互斥锁(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 时打印其值。使用互斥锁来确保对共享变量的安全访问。
二、条件变量
- 条件变量允许一个线程等待特定的条件满足,而另一个线程可以通知条件已满足。
- 通常与互斥锁一起使用,以确保线程安全地访问共享资源和等待条件。
例如:
#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;
}
在这个例子中,生产者线程不断增加共享变量的值,并在每次增加后通知条件变量。消费者线程等待共享变量不为零的条件,当条件满足时,打印共享变量的值。
三、信号量
- 信号量可以用于控制对共享资源的访问,实现线程间的同步和互斥。
- 可以使用 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)是一种用于线程同步的机制,它确保在任何时候只有一个线程可以访问被互斥锁保护的资源。当一个线程获取互斥锁时,其他线程如果试图获取该锁,将被阻塞,直到持有锁的线程释放锁。
二、互斥锁的使用步骤
-
包含头文件:
- 在使用互斥锁之前,需要包含
<pthread.h>
头文件。
- 在使用互斥锁之前,需要包含
-
声明互斥锁变量:
- 使用
pthread_mutex_t
类型声明一个互斥锁变量。
- 使用
-
初始化互斥锁:
- 在使用互斥锁之前,需要使用
pthread_mutex_init
函数对其进行初始化。 - 函数原型:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
- 参数说明:
mutex
是指向互斥锁变量的指针。attr
是互斥锁的属性指针,可以设置为NULL
,使用默认属性。
- 在使用互斥锁之前,需要使用
-
加锁和解锁互斥锁:
- 使用
pthread_mutex_lock
函数对互斥锁进行加锁,确保只有一个线程可以访问被保护的资源。 - 使用
pthread_mutex_unlock
函数对互斥锁进行解锁,允许其他线程获取锁并访问资源。 - 函数原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
- 参数说明:
mutex
是指向互斥锁变量的指针。
- 使用
-
销毁互斥锁:
- 在不再需要互斥锁时,使用
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
进行递增操作。使用互斥锁来确保在任何时候只有一个线程可以访问共享变量,从而避免了竞争条件。
四、注意事项
- 死锁问题:如果多个线程以不同的顺序获取多个互斥锁,可能会导致死锁。在设计程序时,要注意避免死锁的发生。
- 错误处理:在使用互斥锁的函数时,要检查返回值并进行适当的错误处理,以确保程序的稳定性。
- 可重入性:互斥锁应该是可重入的,即同一个线程可以多次获取同一个互斥锁而不会导致死锁。
总之,在 C 语言中,使用互斥锁可以有效地保护共享资源,避免线程之间的竞争条件。在使用互斥锁时,要注意正确地初始化、加锁、解锁和销毁互斥锁,并避免死锁和错误处理问题。
七,线程同步
一、线程同步的概念
二、生产者与消费者模型原理
三,示例代码
#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
表示缓冲区中当前的物品数量。in
和out
分别是生产者放入物品和消费者取出物品的索引。
2、线程同步机制
- 互斥锁(
pthread_mutex_t mutex
):确保在任何时候只有一个线程可以访问共享资源(缓冲区和状态变量)。
3、生产者线程(producer
函数)
- 不断生成随机数作为要生产的物品。
- 使用互斥锁锁定共享资源,当缓冲区已满(
count == BUFFER_SIZE
)时,等待条件变量full
。 - 将物品放入缓冲区,更新索引和物品数量,然后通过信号量通知消费者线程(
pthread_cond_signal(&empty)
)。
4、消费者线程(consumer
函数)
- 不断尝试从缓冲区中取出物品进行消费。
- 使用互斥锁锁定共享资源,当缓冲区为空(
count == 0
)时,等待条件变量empty
。 - 从缓冲区中取出物品,更新索引和物品数量,然后通过信号量通知生产者线程(
pthread_cond_signal(&full)
)。
5、注意事项
- 死锁预防:确保在等待条件变量之前已经持有互斥锁,并且在唤醒其他线程后及时释放互斥锁,以避免死锁的发生。
- 正确的条件判断:在等待条件变量之前,要确保条件判断的准确性。例如,在这个模型中,生产者线程在判断缓冲区已满时,应该使用
while
循环而不是if
语句,以防止虚假唤醒。 - 可扩展性:可以根据实际需求扩展这个模型,例如增加多个生产者和消费者线程,或者修改缓冲区的大小和数据类型。
八,条件变量
简介:
一、条件变量的概念
-
条件变量的作用:
- 条件变量用于解决线程之间的等待和通知问题。当一个线程需要等待某个条件满足时,它可以使用条件变量进行等待。当另一个线程使条件满足时,它可以通知等待的线程继续执行。
- 条件变量通常与互斥锁一起使用,以确保对共享资源的安全访问和正确的等待和通知操作。
-
条件变量的组成:
- 条件变量由一个等待队列和一个信号机制组成。当一个线程调用
pthread_cond_wait
函数等待条件变量时,它会被放入等待队列中,并释放互斥锁。当另一个线程调用pthread_cond_signal
或pthread_cond_broadcast
函数通知条件变量时,等待队列中的一个或多个线程会被唤醒,并重新获取互斥锁。
- 条件变量由一个等待队列和一个信号机制组成。当一个线程调用
二、条件变量的使用步骤
-
包含头文件:
- 在使用条件变量之前,需要包含
<pthread.h>
头文件。
- 在使用条件变量之前,需要包含
-
声明条件变量和互斥锁变量:
- 使用
pthread_cond_t
类型声明一个条件变量变量,使用pthread_mutex_t
类型声明一个互斥锁变量。
- 使用
-
初始化条件变量和互斥锁:
- 在使用条件变量和互斥锁之前,需要使用
pthread_cond_init
和pthread_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
,使用默认属性。
- 在使用条件变量和互斥锁之前,需要使用
-
使用条件变量和互斥锁:
- 在需要等待条件变量的线程中,首先获取互斥锁,然后检查条件是否满足。如果条件不满足,则调用
pthread_cond_wait
函数等待条件变量。 - 在需要通知条件变量的线程中,首先获取互斥锁,然后修改共享资源,使条件满足。最后,调用
pthread_cond_signal
或pthread_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
是指向互斥锁变量的指针。
- 在需要等待条件变量的线程中,首先获取互斥锁,然后检查条件是否满足。如果条件不满足,则调用
-
销毁条件变量和互斥锁:
- 在不再需要条件变量和互斥锁时,使用
pthread_cond_destroy
和pthread_mutex_destroy
函数销毁它们。 - 函数原型:
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
- 参数说明:
cond
和mutex
是指向条件变量变量和互斥锁变量的指针。
- 在不再需要条件变量和互斥锁时,使用
三、示例代码
以下是一个使用条件变量实现生产者 - 消费者模型的示例代码:
#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
上的生产者线程。
四、注意事项
- 正确的等待和通知顺序:在使用条件变量时,要确保等待条件变量的线程在检查条件之前已经获取了互斥锁,并且在等待条件变量时释放了互斥锁。通知条件变量的线程在修改共享资源使条件满足后,再通知等待的线程。
- 避免虚假唤醒:在等待条件变量时,可能会出现虚假唤醒的情况,即线程在没有被其他线程通知的情况下被唤醒。为了避免虚假唤醒,通常在等待条件变量时使用
while
循环而不是if
语句来检查条件。 - 死锁预防:在使用条件变量和互斥锁时,要注意避免死锁的发生。例如,确保在等待条件变量之前已经获取了互斥锁,并且在通知条件变量后及时释放互斥锁。
总之,条件变量是一种强大的线程间同步机制,可以用于解决复杂的同步问题。在使用条件变量时,要注意正确的使用方法和避免常见的错误,以确保程序的正确性和稳定性。