一、线程(thread)的创建、等待、退出
二、线程共享内存空间
三、互斥量(mutex)
四、互斥锁限制共享资源的访问
五、线程死锁(Thread Deadlock)
六、线程条件控制(Thread Condition Control)
一、线程(thread)的创建、等待、退出
线程相关的API
线程ID获取及比较
pthread_t pthread_self(void);
创建线程
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t*restrict attr, void *(*start_rtn)(void *), void *restrict arg);
等待线程
int pthread_join(pthread_t thread, void **rval_ptr);
线程退出
int pthread_exit(void *rval_ptr);
API调用
#include <stdio.h>
#include <pthread.h>
// 线程ID获取及比较
// pthread_t pthread_self(void);
// pthread_create函数的原型创建线程
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// pthread_join函数的原型等待线程
// int pthread_join(pthread_t thread, void **rval_ptr);
// 线程函数,将在新线程中执行
void *func1(void *arg)
{
// 打印新线程的标识符(ID)
printf("t1:%ld thread is created\n", (unsigned long)pthread_self());
// 打印传递给线程的参数
printf("t1:param is %d\n", *((int *)arg));
}
int main()
{
int ret;
int param = 100;
pthread_t t1;
// 创建一个新线程,传递线程标识符给t1,执行func1函数,参数为param
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
// 如果线程创建成功,打印成功信息
printf("main: Created t1 successfully\n");
}
// 打印主线程的标识符(ID)
printf("main:%ld\n", (unsigned long)pthread_self());
// 主线程等待t1线程的结束
pthread_join(t1, NULL);
return 0;
}
#include <stdio.h>
#include <pthread.h>
// 线程ID获取及比较
// pthread_t pthread_self(void);
// pthread_create函数的原型创建线程
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// pthread_join函数的原型等待线程
// int pthread_join(pthread_t thread, void **rval_ptr);
// pthread_exit函数的原型线程退出
// int pthread_exit(void *rval_ptr);
// 线程函数,将在新线程中执行
void *func1(void *arg)
{
// 声明并初始化一个静态整数变量ret
static int ret = 10;
// 打印新线程的标识符(ID)
printf("t1:%ld thread is created\n", (unsigned long)pthread_self());
// 打印传递给线程的参数
printf("t1:param is %d\n", *((int *)arg));
// 退出线程并返回指向ret的指针
pthread_exit((void *)&ret);
}
int main()
{
int ret;
int param = 100;
pthread_t t1;
int *pret = NULL;
// 创建一个新线程,传递线程标识符给t1,执行func1函数,参数为param
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
// 如果线程创建成功,打印成功信息
printf("main: Created t1 successfully\n");
}
// 打印主线程的标识符(ID)
printf("main:%ld\n", (unsigned long)pthread_self());
// 主线程等待t1线程的结束,并获取线程退出状态
pthread_join(t1, (void **)&pret);
// 打印t1线程退出时的ret值
printf("main: t1 quit: %d\n", *pret);
return 0;
}
线程传递一个字符串
#include <stdio.h>
#include <pthread.h>
// 线程ID获取及比较
// pthread_t pthread_self(void);
// pthread_create函数的原型创建线程
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// pthread_join函数的原型等待线程
// int pthread_join(pthread_t thread, void **rval_ptr);
// pthread_exit函数的原型线程退出
// int pthread_exit(void *rval_ptr);
// 线程函数,将在新线程中执行
void *func1(void *arg)
{
// 声明一个指向字符串的静态指针p
static char *p = "t1 is run out";
// 打印新线程的标识符(ID)
printf("t1:%ld thread is created\n", (unsigned long)pthread_self());
// 打印传递给线程的参数
printf("t1:param is %d\n", *((int *)arg));
// 退出线程并返回指向字符串p的指针
pthread_exit((void *)p);
}
int main()
{
int ret;
int param = 100;
pthread_t t1;
int *pret = NULL;
// 创建一个新线程,传递线程标识符给t1,执行func1函数,参数为param
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
// 如果线程创建成功,打印成功信息
printf("main: Created t1 successfully\n");
}
// 打印主线程的标识符(ID)
printf("main:%ld\n", (unsigned long)pthread_self());
// 主线程等待t1线程的结束,并获取线程退出状态
pthread_join(t1, (void **)&pret);
// 打印t1线程退出时的ret值
printf("main: t1 quit: %d\n", *pret);
// 打印t1线程退出时的字符串
printf("main: t1 quit: %s\n", pret);
return 0;
}
二、线程共享内存空间
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
/*
线程ID获取及比较
pthread_t pthread_self(void);
pthread_create函数的原型创建线程
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr,
void *(*start_rtn)(void *), void *restrict arg);
pthread_join函数的原型等待线程
int pthread_join(pthread_t thread, void **rval_ptr);
pthread_exit函数的原型线程退出
int pthread_exit(void *rval_ptr);
*/
int g_data = 0; // 全局变量,多个线程可以访问和修改
// 线程函数1,将在新线程中执行
void *func1(void *arg)
{
printf("t1:%ld thread is created\n", (unsigned long)pthread_self());
printf("t1:param is %d\n", *((int *)arg));
while (1) {
printf("t1: %d\n", g_data++); // 打印全局变量g_data并递增
sleep(1);
if (g_data == 3) {
pthread_exit(NULL); // 当g_data等于3时,退出线程
}
}
}
// 线程函数2,将在新线程中执行
void *func2(void *arg)
{
printf("t2:%ld thread is created\n", (unsigned long)pthread_self());
printf("t2:param is %d\n", *((int *)arg));
while (1) {
printf("t2: %d\n", g_data++); // 打印全局变量g_data并递增
sleep(1);
}
}
int main()
{
int ret;
int param = 100;
pthread_t t1;
pthread_t t2;
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
printf("main: Created t1 successfully\n");
}
ret = pthread_create(&t2, NULL, func2, (void *)¶m);
if (ret == 0) {
printf("main: Created t2 successfully\n");
}
printf("main:%ld\n", (unsigned long)pthread_self());
while (1) {
printf("main: %d\n", g_data++); // 打印全局变量g_data并递增
sleep(1);
}
pthread_join(t1, NULL); // 主线程等待t1线程的结束
pthread_join(t2, NULL); // 主线程等待t2线程的结束
return 0;
}
t1要获得3才会退出如果t2拿走t1就不会退出
三、互斥量(mutex)
与互斥锁相关API
创建互斥锁(初始化互斥锁)
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
加锁,防止多个线程同时执行下面的代码块
int pthread_mutex_lock(pthread_mutex_t *mutex);
解锁,允许其他线程访问被保护的代码块
int pthread_mutex_unlock(pthread_mutex_t *mutex);
API调用
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
/*
线程ID获取及比较
pthread_t pthread_self(void);
pthread_create函数的原型创建线程
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
pthread_join函数的原型等待线程
int pthread_join(pthread_t thread, void **rval_ptr);
pthread_exit函数的原型线程退出
int pthread_exit(void *rval_ptr);
*/
/*
创建互斥锁(初始化互斥锁)
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
加锁,防止多个线程同时执行下面的代码块
int pthread_mutex_lock(pthread_mutex_t *mutex);
解锁,允许其他线程访问被保护的代码块
int pthread_mutex_unlock(pthread_mutex_t *mutex);
*/
pthread_mutex_t mutex; // 声明一个互斥锁
void *func1(void *arg)
{
int i;
pthread_mutex_lock(&mutex); // 加锁,防止多个线程同时执行下面的代码块
for (i = 0; i < 5; i++) {
printf("t1:%ld thread is created\n", (unsigned long)pthread_self());
printf("t1:param is %d\n", *((int *)arg));
sleep(1);
}
pthread_mutex_unlock(&mutex); // 解锁,允许其他线程访问被保护的代码块
}
void *func2(void *arg)
{
pthread_mutex_lock(&mutex); // 加锁
printf("t2:%ld thread is created\n", (unsigned long)pthread_self());
printf("t2:param is %d\n", *((int *)arg));
pthread_mutex_unlock(&mutex); // 解锁
}
void *func3(void *arg)
{
pthread_mutex_lock(&mutex); // 加锁
printf("t3:%ld thread is created\n", (unsigned long)pthread_self());
printf("t3:param is %d\n", *((int *)arg));
pthread_mutex_unlock(&mutex); // 解锁
}
int main()
{
int ret;
int param = 100;
pthread_t t1;
pthread_t t2;
pthread_t t3;
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
printf("main: Created t1 successfully\n");
}
ret = pthread_create(&t2, NULL, func2, (void *)¶m);
if (ret == 0) {
printf("main: Created t2 successfully\n");
}
ret = pthread_create(&t3, NULL, func3, (void *)¶m);
printf("main:%ld\n", (unsigned long)pthread_self());
pthread_join(t1, NULL); // 主线程等待t1线程的结束
pthread_join(t2, NULL); // 主线程等待t2线程的结束
pthread_mutex_destroy(&mutex); // 销毁互斥锁
return 0;
}
四、互斥锁限制共享资源的访问
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
/*
线程ID获取及比较
pthread_t pthread_self(void);
pthread_create函数的原型创建线程
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
pthread_join函数的原型等待线程
int pthread_join(pthread_t thread, void **rval_ptr);
pthread_exit函数的原型线程退出
int pthread_exit(void *rval_ptr);
*/
/*
创建互斥锁(初始化互斥锁)
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
加锁,防止多个线程同时执行下面的代码块
int pthread_mutex_lock(pthread_mutex_t *mutex);
解锁,允许其他线程访问被保护的代码块
int pthread_mutex_unlock(pthread_mutex_t *mutex);
*/
int g_data = 0; // 全局变量,多个线程可以访问和修改
pthread_mutex_t mutex; // 互斥锁,用于保护共享资源
void *func1(void *arg)
{
printf("t1:%ld thread is created\n", (unsigned long)pthread_self());
printf("t1:param is %d\n", *((int *)arg));
pthread_mutex_lock(&mutex); // 加锁,防止多个线程同时访问g_data
while (1) {
printf("t1: %d\n", g_data++); // 打印全局变量g_data并递增
sleep(1);
if (g_data == 3) {
pthread_mutex_unlock(&mutex); // 解锁互斥锁
printf("t1 quit================================\n");
exit(0); // 退出线程
}
}
}
void *func2(void *arg)
{
printf("t2:%ld thread is created\n", (unsigned long)pthread_self());
printf("t2:param is %d\n", *((int *)arg));
while (1) {
printf("t2: %d\n", g_data);
pthread_mutex_lock(&mutex); // 加锁,防止多个线程同时修改g_data
g_data++; // 修改全局变量g_data
pthread_mutex_unlock(&mutex); // 解锁互斥锁
sleep(1);
}
}
int main()
{
int ret;
int param = 100;
pthread_t t1;
pthread_t t2;
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
printf("main: Created t1 successfully\n");
}
ret = pthread_create(&t2, NULL, func2, (void *)¶m);
if (ret == 0) {
printf("main: Created t2 successfully\n");
}
printf("main:%ld\n", (unsigned long)pthread_self());
while (1) {
printf("main: %d\n", g_data); // 打印全局变量g_data
sleep(1);
}
pthread_join(t1, NULL); // 主线程等待t1线程的结束
pthread_join(t2, NULL); // 主线程等待t2线程的结束
pthread_mutex_destroy(&mutex); // 销毁互斥锁
return 0;
}
chomd -x test.sh
五、线程死锁(Thread Deadlock)
线程死锁(Thread Deadlock)是多线程编程中一种常见的问题,它发生在两个或多个线程相互等待对方释放资源的情况下,导致所有线程都无法继续执行。死锁通常发生在线程试图获取多个互斥锁或资源时,而不恰当地等待其他线程释放资源。
以下是导致线程死锁的一般条件,通常被称为死锁四条件:
-
互斥条件:至少有一个资源被多个线程竞争。这意味着资源是排他性的,只能由一个线程同时使用。
-
占有并等待条件:线程已经获取了至少一个资源,但还希望获取更多资源,并且在等待其他线程释放资源时被阻塞。
-
不可剥夺条件:资源不能被线程强制释放,只能由持有资源的线程显式释放。
-
环路等待条件:一组线程互相等待资源,每个线程等待的资源都被下一个线程持有,形成了一个环路等待的情况。
死锁的发生通常需要同时满足这四个条件。一旦死锁发生,它将导致程序停滞,因为没有线程能够继续执行。解决线程死锁的方法通常包括以下策略:
-
避免死锁:设计线程互斥访问资源的顺序,以最大程度地减少死锁的可能性。此外,可以使用一些算法和策略来避免死锁,如银行家算法。
-
检测和恢复:实施死锁检测机制,以识别死锁情况,并在检测到死锁时采取适当的措施,如终止某些线程或回滚操作。
-
资源分配策略:为资源分配引入优先级或超时策略,以确保某些线程不会无限等待资源。
-
明确资源释放:确保线程在使用完资源后及时释放资源,以防止资源泄漏。
死锁是多线程编程的一个复杂问题,需要仔细的规划和处理,以确保应用程序的稳定性和可靠性。
六、线程条件控制(Thread Condition Control)
线程条件控制(Thread Condition Control)是多线程编程中的一种重要机制,用于线程之间的协作和同步。条件控制提供了一种方式,允许一个线程等待某个条件的发生,而其他线程可以在满足条件时通知等待线程。它通常与互斥锁结合使用,以确保线程在访问共享资源之前等待适当的条件。
在C/C++中,条件控制通常是使用以下两个相关的数据结构和函数实现的:
-
条件变量(Condition Variable):条件变量是一个用于线程之间通信的数据结构。它允许一个线程等待某个条件的发生,而其他线程可以在满足条件时发出通知。条件变量通常与互斥锁一起使用,以确保在检查条件和等待条件之间的操作是原子的。
- 创建条件变量:使用
pthread_cond_init
函数初始化条件变量。 - 等待条件:使用
pthread_cond_wait
函数使线程等待条件的发生。 - 发出通知:使用
pthread_cond_signal
或pthread_cond_broadcast
函数通知等待的线程条件已发生。 - 销毁条件变量:使用
pthread_cond_destroy
函数销毁条件变量。
- 创建条件变量:使用
-
互斥锁(Mutex):互斥锁用于保护共享资源,以确保在检查条件和等待条件之间的操作是原子的。通常,线程在等待条件时会释放互斥锁,以允许其他线程访问共享资源。
- 创建互斥锁:使用
pthread_mutex_init
函数初始化互斥锁。 - 锁定互斥锁:使用
pthread_mutex_lock
函数锁定互斥锁。 - 解锁互斥锁:使用
pthread_mutex_unlock
函数解锁互斥锁。 - 销毁互斥锁:使用
pthread_mutex_destroy
函数销毁互斥锁。
- 创建互斥锁:使用
基本的条件控制流程如下:
- 线程A获取互斥锁并检查条件。
- 如果条件不满足,线程A释放互斥锁并等待条件变量。
- 线程B或其他线程满足了条件后,发出通知。
- 等待的线程A被唤醒,再次获取互斥锁,检查条件是否满足。
- 如果条件满足,线程A继续执行。
这个机制允许线程在需要等待某些条件的发生时挂起,而不是无休止地忙等。条件控制在并发编程中用于实现各种同步和协作场景,如生产者-消费者问题、读写锁、任务队列等。
当处理多线程程序时,条件控制机制是一种重要的工具,用于协调线程之间的操作。下面是一个使用条件控制的示例,并附有详细的注释,以帮助理解:
#include <stdio.h>
#include <pthread.h>
int data = 0; // 全局变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥锁
pthread_cond_t condition = PTHREAD_COND_INITIALIZER; // 初始化条件变量
void *producer(void *arg)
{
for (int i = 0; i < 5; i++) {
pthread_mutex_lock(&mutex); // 锁定互斥锁
data = i; // 生产数据
printf("Producer: Produced %d\n", data);
pthread_cond_signal(&condition); // 发出条件满足的信号
pthread_mutex_unlock(&mutex); // 解锁互斥锁
}
pthread_exit(NULL);
}
void *consumer(void *arg)
{
for (int i = 0; i < 5; i++) {
pthread_mutex_lock(&mutex); // 锁定互斥锁
while (data < i) {
pthread_cond_wait(&condition, &mutex); // 等待条件满足
}
printf("Consumer: Consumed %d\n", data);
pthread_mutex_unlock(&mutex); // 解锁互斥锁
}
pthread_exit(NULL);
}
int main()
{
pthread_t producer_thread, consumer_thread;
// 创建生产者和消费者线程
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(&condition);
return 0;
}
这个示例演示了一个简单的生产者-消费者问题,其中生产者线程生产数据,而消费者线程消费数据。互斥锁用于保护共享的data
变量,而条件变量用于在数据可用时通知消费者线程。注释提供了对每个关键步骤的解释,以帮助理解条件控制的工作原理。
与条件变量相关API
线程条件变量的API通常是由POSIX线程库(pthread)提供的,用于实现线程之间的条件协作。以下是常见的线程条件变量相关函数:
- 初始化条件变量:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
cond
:指向要初始化的条件变量的指针。attr
:条件变量的属性,通常设置为NULL以使用默认属性。- 返回:成功返回0,失败返回错误码。
- 销毁条件变量:
int pthread_cond_destroy(pthread_cond_t *cond);
cond
:指向要销毁的条件变量的指针。- 返回:成功返回0,失败返回错误码。
- 等待条件变量满足,同时释放互斥锁并阻塞当前线程。
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
- 等待条件变量满足,同时释放互斥锁并阻塞当前线程。
cond
:指向条件变量的指针。mutex
:指向互斥锁的指针,必须与pthread_cond_signal
和pthread_cond_broadcast
使用的互斥锁相同。- 返回:成功返回0,失败返回错误码。
- 在指定的时间内等待条件变量满足,同时释放互斥锁并阻塞当前线程。
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
cond
:指向条件变量的指针。mutex
:指向互斥锁的指针,必须与pthread_cond_signal
和pthread_cond_broadcast
使用的互斥锁相同。abstime
:指定的等待时间,如果超时仍未满足条件,将返回。- 返回:成功返回0,失败返回错误码。
- 发送条件满足的信号给等待的线程中的一个线程,以唤醒其中一个线程。
int pthread_cond_signal(pthread_cond_t *cond);
cond
:指向条件变量的指针。- 返回:成功返回0,失败返回错误码。
- 发送条件满足的信号给等待的线程中的所有线程,以唤醒所有等待的线程。
int pthread_cond_broadcast(pthread_cond_t *cond);cond
:指向条件变量的指针。- 返回:成功返回0,失败返回错误码。
这些函数是用于创建、销毁条件变量、等待条件满足、发出信号等操作的常见API。条件变量通常与互斥锁一起使用,以确保线程在等待条件变量时能够释放互斥锁,允许其他线程访问共享资源。条件变量是实现线程同步和协作的重要工具。
API调用
1创建条件变量:使用pthread_cond_init函数初始化条件变量。
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
2等待条件:使用pthread_cond_wait函数使线程等待条件的发生。
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
-等待条件变量满足,同时释放互斥锁并阻塞当前线程
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
-在指定的时间内等待条件变量满足,同时释放互斥锁并阻塞当前线程
3发出通知:使用pthread_cond_signal或pthread_cond_broadcast函数通知等待的线程条件已发生。
int pthread_cond_signal(pthread_cond_t *cond);
-发送条件满足的信号给等待的线程中的一个线程,以唤醒其中一个线程
int pthread_cond_broadcast(pthread_cond_t *cond);
-发送条件满足的信号给等待的线程中的所有线程,以唤醒所有等待的线程
4销毁条件变量:使用pthread_cond_destroy函数销毁条件变量。
int pthread_cond_destroy(pthread_cond_t *cond);
测试打印调试信息
//testdemo.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int time = atoi(argv[1]);
for (int i = 0; i < time; i++){
system("./testdemo");//要执行的程序
}
}
./a.out 10 >> testdemo_log.txt &
静态宏初始化和动态初始化
静态宏初始化和动态初始化是用于初始化线程同步原语(例如互斥锁和条件变量)的两种不同方法。
-
静态宏初始化:
使用静态宏初始化时,你可以在定义线程同步变量时使用宏,例如:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
这些宏是POSIX线程库的一部分,它们提供了一种在编译时进行初始化的方式。这意味着你可以在定义变量的同时将其初始化为合适的默认值,而不必在运行时调用初始化函数。
PTHREAD_MUTEX_INITIALIZER
和PTHREAD_COND_INITIALIZER
是静态宏,它们用于初始化互斥锁和条件变量,并将它们设置为默认初始状态。优点:
- 静态宏初始化简单,可以在定义时一并进行初始化。
- 适用于一些简单的线程同步变量。
缺点:
- 不适用于需要在运行时动态配置的情况。
- 不能提供额外的初始化选项。
-
动态初始化:
使用动态初始化时,你需要在运行时调用初始化函数来初始化线程同步变量。例如,对于互斥锁和条件变量,你可以使用
pthread_mutex_init
和pthread_cond_init
函数进行初始化。pthread_mutex_t mutex; pthread_cond_t condition; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&condition, NULL);
动态初始化适用于更复杂的情况,可以在初始化时提供更多的选项和属性。你可以使用不同的属性对象来配置线程同步变量的行为。
优点:
- 适用于需要在运行时进行初始化和配置的情况。
- 具有更多的灵活性,可以根据需要设置属性。
缺点:
- 需要额外的初始化步骤,涉及函数调用。
- 更复杂,需要处理初始化和销毁。
在选择静态宏初始化或动态初始化时,要考虑你的需求。对于简单的情况,静态宏初始化可能足够,而对于复杂的情况,可能需要使用动态初始化来灵活配置线程同步变量。
生产者-消费者问题中的同步问题
生产者-消费者问题中的同步问题主要涉及如何协调生产者和消费者线程,以避免以下情况:
-
数据竞争:多个线程同时访问和修改共享的缓冲区可能导致数据不一致性和错误。为了解决这个问题,需要确保一次只有一个线程可以访问或修改缓冲区。
-
阻塞和死锁:如果没有适当的同步机制,生产者和消费者线程可能会在缓冲区为空或满时阻塞,导致死锁情况。死锁是指线程永远无法继续执行的情况。
为解决这些同步问题,通常使用以下同步工具和技巧:
-
互斥锁(Mutex):互斥锁用于保护共享资源(如缓冲区),确保同时只有一个线程可以访问它。在生产者将数据放入缓冲区或消费者从缓冲区取出数据时,需要获得互斥锁。
-
条件变量(Condition Variable):条件变量用于在线程之间发送信号和通知。在生产者等待缓冲区不满或消费者等待缓冲区不空时,条件变量被用来通知等待的线程。这有助于避免不必要的忙等待。
-
缓冲区:缓冲区是共享的数据结构,用于在生产者和消费者之间传递数据。缓冲区的大小限制了可以存储的数据数量。
-
同步信号:生产者和消费者线程之间需要发送和接收同步信号,以确定何时放置数据或取出数据。
基本的同步过程如下:
- 当缓冲区已满时,生产者线程等待条件变量,直到有消费者线程取出数据并通知它。
- 当缓冲区为空时,消费者线程等待条件变量,直到有生产者线程放入数据并通知它。
- 在生产者放入数据或消费者取出数据时,需要获得互斥锁以防止数据竞争。
通过正确实现这些同步机制,可以协调生产者和消费者线程的操作,避免数据竞争和死锁,确保线程之间的正确执行和数据一致性。
示例
以下是一个C语言示例,演示了如何使用互斥锁和条件变量解决生产者-消费者问题的同步问题。在这个示例中,有一个有限大小的缓冲区,一个生产者线程和一个消费者线程。
#include <stdio.h>
#include <pthread.h>
#define BUFFER_SIZE 5
pthread_mutex_t mutex;
pthread_cond_t buffer_not_full, buffer_not_empty;
int buffer[BUFFER_SIZE];
int count = 0;
void *producer(void *arg) {
int item = 1;
while (1) {
pthread_mutex_lock(&mutex);
while (count == BUFFER_SIZE) {
pthread_cond_wait(&buffer_not_full, &mutex);
}
buffer[count++] = item;
printf("Produced: %d\n", item++);
pthread_cond_signal(&buffer_not_empty);
pthread_mutex_unlock(&mutex);
}
}
void *consumer(void *arg) {
while (1) {
pthread_mutex_lock(&mutex);
while (count == 0) {
pthread_cond_wait(&buffer_not_empty, &mutex);
}
int item = buffer[--count];
printf("Consumed: %d\n", item);
pthread_cond_signal(&buffer_not_full);
pthread_mutex_unlock(&mutex);
}
}
int main() {
pthread_t producer_thread, consumer_thread;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&buffer_not_full, NULL);
pthread_cond_init(&buffer_not_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(&buffer_not_full);
pthread_cond_destroy(&buffer_not_empty);
return 0;
}
在这个示例中,生产者线程负责将数据放入缓冲区,消费者线程负责从缓冲区中取出数据。互斥锁用于保护对缓冲区的访问,条件变量用于通知线程何时可以执行生产或消费操作。通过这些同步机制,线程能够正确地协作,避免数据竞争和死锁,确保正确的执行顺序和数据一致性。