22.线程同步

发布时间 2023-09-06 18:01:19作者: CodeMagicianT

22.线程同步

学习目标:

  • 熟练掌握互斥量的使用

  • 说出什么叫死锁以及解决方案

  • 熟练掌握读写锁的使用

  • 熟练掌握条件变量的使用

  • 理解条件变量实现的生产消费者模型

理解信号量实现的生产消费者模型

1.互斥锁

1.1互斥锁的使用步骤

  • 第1步:创建一把互斥锁

 ▶pthread_mutex_t mutex;

  • 初始化互斥锁

 ▶pthread_mutex_init(&mutex);---相当于mutex=1

  • 在代码中寻找共享资源(也称为临界区)

 pthread_mutex_lock(&mutex); -- mutex = 0

 [临界区代码]

 pthread_mutex_unlock(&mutex); -- mutex = 1

  • 释放互斥锁资源

 pthread_mutex_destroy(&mutex);

注意:必须在所有操作共享资源的线程上都加上锁否则不能起到同步的效果。

1.2 练习

  • 编写思路:

1 定义一把互斥锁,应该为一全局变量

 pthread_mutex_t mutex;

2 在main函数中对mutex进行初始化

 pthread_mutex_init(&mutex, NULL);

3 创建两个线程,在两个线程中加锁和解锁

4 主线程释放互斥锁资源

 pthread_mutex_destroy(&mutex);

加锁

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>

//定义一把锁
pthread_mutex_t mutex;

void* mythread1(void* args)
{
	while (1)
	{
		//加锁
		//pthread_mutex_lock(&mutex);

		printf("hello ");
		sleep(rand() % 3);
		printf("world\n");

		//解锁
		//pthread_mutex_unlock(&mutex);
		sleep(rand() % 3);
	}

	pthread_exit(NULL);
}


void* mythread2(void* args)
{
	while (1)
	{
		//加锁
		//pthread_mutex_lock(&mutex);

		printf("HELLO ");
		sleep(rand() % 3);
		printf("WORLD\n");

		//解锁
		//pthread_mutex_unlock(&mutex);
		sleep(rand() % 3);
	}

	pthread_exit(NULL);
}

int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;

	//随机数种子
	srand(time(NULL));

	//互斥锁初始化
	pthread_mutex_init(&mutex, NULL);

	ret = pthread_create(&thread1, NULL, mythread1, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	ret = pthread_create(&thread2, NULL, mythread2, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//等待线程结束
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);

	//释放互斥锁
	pthread_mutex_destroy(&mutex);
	return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>

//定义一把锁
pthread_mutex_t mutex;

void* mythread1(void* args)
{
	while (1)
	{
		//加锁
		pthread_mutex_lock(&mutex);

		printf("hello ");
		sleep(rand() % 3);
		printf("world\n");

		//解锁
		pthread_mutex_unlock(&mutex);
		sleep(rand() % 3);
	}

	pthread_exit(NULL);
}


void* mythread2(void* args)
{
	while (1)
	{
		//加锁
		pthread_mutex_lock(&mutex);

		printf("HELLO ");
		sleep(rand() % 3);
		printf("WORLD\n");

		//解锁
		pthread_mutex_unlock(&mutex);
		sleep(rand() % 3);
	}

	pthread_exit(NULL);
}

int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;

	//随机数种子
	srand(time(NULL));

	//互斥锁初始化
	pthread_mutex_init(&mutex, NULL);

	ret = pthread_create(&thread1, NULL, mythread1, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	ret = pthread_create(&thread2, NULL, mythread2, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//等待线程结束
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);

	//释放互斥锁    
	pthread_mutex_destroy(&mutex);
	return 0;
}

1.3死锁

死锁并不是linux提供给用户的一种使用方法,而是由于用户使用互斥锁不当引起的一种现象。

  • 常见的死锁有两种:

 ▶第一种:自己锁自己,如下图代码片段

下面也会

void* mythread1(void* args)
{
	while (1)
	{
		//加锁
		pthread_mutex_lock(&mutex);

		printf("hello ");
		sleep(rand() % 3);
		printf("world\n");

		//解锁
		//pthread_mutex_unlock(&mutex);
		sleep(rand() % 3);
	}

	pthread_exit(NULL);
}

void* mythread2(void* args)
{
	while (1)
	{
		//加锁
		pthread_mutex_lock(&mutex);

		printf("HELLO ");
		sleep(rand() % 3);
		printf("WORLD\n");

		//解锁
		pthread_mutex_unlock(&mutex);
		sleep(rand() % 3);
	}

	pthread_exit(NULL);
}

 ▶第二种 线程A拥有A锁,请求获得B锁;线程B拥有B锁,请求获得A锁,这样造成线程A和线程B都不释放自己的锁,而且还想得到对方的锁,从而产生死锁,如下图所示:

  • 如何解决死锁:

 ▶让线程按照一定的顺序去访问共享资源

 ▶在访问其他锁的时候,需要先将自己的锁解开

 ▶调用pthread_mutex_trylock,如果加锁不成功会立刻返回

死锁: 死锁不是linux提供给开发者的一种机制,而是由于开发者操作不当引起的。
1.自己锁自己.
 注意点: 线程在异常退出的时候也需要解锁.
2.A线程占用着A锁,又想去获得B锁;B线程占用着B锁,又想去获得A锁,两个线程都不释放自己的锁,又想去获得对方的锁,从而造成了死锁。
解决方法:
 a.需要先释放自己的锁再去获得其他锁
 b.避免使用嵌套的锁,让线程按照一定的顺序加锁
 c.可以调用pthread_mutex_trylock函数加锁,该函数不阻塞,所以不会产生死锁。

2.读写锁

  • 什么是读写锁

 ▶读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。

  • 读写锁使用场合

 ▶读写锁非常适合于对数据结构读的次数远大于写的情况。

  • 读写锁特性

 ▶读写锁是“写模式加锁”时,解锁前,所有对该锁加锁的线程都会被阻塞。

 ▶读写锁是“读模式加锁”时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。

 ▶读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高

  • 读写锁场景练习:

 ▶线程A加写锁成功, 线程B请求读锁

  ▷线程B阻塞

 ▶线程A持有读锁, 线程B请求写锁

  ▷线程B阻塞

 ▶线程A拥有读锁, 线程B请求读锁

  ▷线程B加锁成功

 ▶线程A持有读锁, 然后线程B请求写锁, 然后线程C请求读锁

  ▷B阻塞,c阻塞 - 写的优先级高

  ▷A解锁,B线程加写锁成功,C继续阻塞

  ▷B解锁,C加读锁成功

 ▶线程A持有写锁, 然后线程B请求读锁, 然后线程C请求写锁

  ▷BC阻塞

  ▷A解锁,C加写锁成功,B继续阻塞

  ▷C解锁,B加读锁成功

  • 读写锁总结

 读并行,写独占,当读写同时等待锁的时候写的优先级高

  • 读写锁主要操作函数

  ▶定义一把读写锁

   ▷pthread_rwlock_t rwlock;

  ▶初始化读写锁

   ▷int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

   ▷函数参数

    ◆rwlock-读写锁

    ◆attr-读写锁属性,传NULL为默认属性

  ▇销毁读写锁

   int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

  ▇加读锁

   int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

  ▇尝试加读锁

   int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

  ▇加写锁

   int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

  ▇尝试加写锁

   int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

  ▇解锁

   int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);

  • 练习:3个线程不定时写同一全局资源,5个线程不定时读同一全局资源。
//读写锁测试程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

int number = 0;

//定义一把读写锁
pthread_rwlock_t rwlock;

//写线程回调函数
void* thread_write(void* arg)
{
	int i = *(int*)arg;

	int cur;

	while (1)
	{
		//加写锁
		pthread_rwlock_wrlock(&rwlock);

		cur = number;
		cur++;
		number = cur;
		printf("[%d]-W:[%d]\n", i, cur);

		//解锁
		pthread_rwlock_unlock(&rwlock);
		sleep(rand() % 3);
	}
}

//读线程回调函数
void* thread_read(void* arg)
{
	int i = *(int*)arg;
	int cur;

	while (1)
	{
		//加读锁
		pthread_rwlock_rdlock(&rwlock);

		cur = number;
		printf("[%d]-R:[%d]\n", i, cur);

		//解锁
		pthread_rwlock_unlock(&rwlock);
		sleep(rand() % 3);
	}
}

int main()
{
	int n = 8;
	int i = 0;
	int arr[8];
	pthread_t thread[8];

	//读写锁初始化
	pthread_rwlock_init(&rwlock, NULL);

	//创建3个写子线程
	for (i = 0; i < 3; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_write, &arr[i]);
	}

	//创建5个读子线程
	for (i = 3; i < n; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_read, &arr[i]);
	}

	//回收子线程
	int j = 0;
	for (j = 0; j < n; j++)
	{
		pthread_join(thread[j], NULL);
	}

	//释放锁
	pthread_rwlock_destroy(&rwlock);

	return 0;
}

读写锁:读写锁是一把锁
 读写锁场景练习:

█线程A加写锁成功,线程B请求读锁
线程B阻塞,当线程A解锁之后,线程B加锁成功
█线程A持有读锁,线程B请求写锁
线程B会阻塞;
当线程A解锁之后,线程B加锁成功
█线程A拥有读锁,线程B请求读锁
线程B请求锁成功
█线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁
线程B和C都阻塞;
当A释放锁之后,B先获得锁,C阻塞
当B释放锁之后,C获得锁
█线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁
线程B和C都阻塞;
当线程A解锁之后,C先获得锁,B阻塞;
当C解锁之后,B获得锁

读写锁总结
写独占,读共享,当读和写一起等待锁的时候,写的优先级高

读写锁使用步骤:
1.先定义一把读写锁:
pthread_rwlock_t rwlock;
2.初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
3.加锁
pthread_rwlock_rdlock(&rwlock);---->加读锁
pthread_rwlock_wrlock(&rwlock);---->加写锁
////////////////////////
共享资源出现的位置
/////////////////////////
4.解锁
pthread_rwlock_unlock(&rwlock);
5.释放锁
pthread_rwlock_destroy(&rwlock);

//读写锁测试程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

int number = 0;

//写线程回调函数
void* thread_write(void* arg)
{
	int i = *(int*)arg;

	int cur;

	while (1)
	{
		cur = number;
		cur++;
		usleep(500);
		number = cur;
		printf("[%d]-W:[%d]\n", i, cur);
	}
}

//读线程回调函数
void* thread_read(void* arg)
{
	int i = *(int*)arg;
	int cur;

	while (1)
	{
		cur = number;
		printf("[%d]-R:[%d]\n", i, cur);
		usleep(500);
	}
}

int main()
{
	int n = 8;
	int i = 0;
	int arr[8];
	pthread_t thread[8];

	//创建3个写子线程
	for (i = 0; i < 3; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_write, &arr[i]);
	}

	//创建5个读子线程
	for (i = 3; i < n; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_read, &arr[i]);
	}

	//回收子线程
	int j = 0;
	for (j = 0; j < n; j++)
	{
		pthread_join(thread[j], NULL);
	}

	return 0;
}

这段代码主要测试多线程下的读写问题。它创建了3个写线程和5个读线程,它们共享同一个全局变量number进行读和写操作。

  1. 写线程回调函数 (thread_write)

    • 此函数首先读取全局变量number的值。
    • 然后将此值增加1。
    • 通过usleep(500)模拟了一个延迟(500微秒),此时其他线程有机会执行。
    • 更新全局变量number
    • 打印线程ID和增加后的值。
  2. 读线程回调函数 (thread_read)

    • 此函数只是读取全局变量number的值并打印。
    • 同样地,通过usleep(500)进行了延迟。
  3. main 函数

    • 创建3个写线程。
    • 创建5个读线程。
    • 使用pthread_join来回收所有线程。

分析

  • 竞态条件:此代码有一个明显的竞态条件,它出现在写线程中。当一个写线程读取number的值并暂停(因为usleep)时,其他线程可能会改变number的值。当原始线程恢复并写入新值时,它可能会覆盖另一个线程的更改。这导致数据不一致和不可预测的结果。

  • 读线程与写线程之间的冲突:由于此代码中没有任何形式的同步机制(如互斥锁或读写锁),读线程可能在一个写线程更改number时读取number的值,这同样也会导致不可预测的结果。

  • 期望结果:由于上述问题,当你运行这个程序时,你会看到写线程可能不会每次都递增number,因为它们的更改可能被其他写线程覆盖。而读线程则可能在写线程之间读取number,从而产生不一致的读取值。

改进

为了避免上述问题,你应该在读取或写入number时使用同步机制。考虑使用pthread的读写锁(pthread_rwlock_t)。读写锁允许多个读线程同时访问,但只允许一个写线程在没有读线程时访问,这正是这种情况所需的。

简而言之,这个程序展示了在没有适当同步的多线程环境中可能会出现的问题。

什么时候运行写线程,什么时候运行读线程

在给出的代码中,写线程和读线程的启动和执行完全取决于操作系统的线程调度器。因为没有明确的同步机制或条件变量来决定哪个线程应该何时运行,所以线程的执行顺序是不确定的。

以下是关于写线程和读线程在此代码中可能的运行模式的简要概述:

  1. 写线程:在main函数中,前三个线程是写线程,它们由thread_write函数控制。这些线程首先读取全局变量number,然后增加它,模拟延迟(通过usleep(500)),并写回新的值。

  2. 读线程:在main函数中,接下来的五个线程是读线程,由thread_read函数控制。这些线程只是读取number的值并打印。

由于线程的执行取决于操作系统的线程调度器,以下是可能的几种情况:

  • 一个或多个写线程可能先运行,然后读线程运行。
  • 一个或多个读线程可能先运行,然后写线程运行。
  • 读写线程之间可能交替运行。

再次强调,因为给定的代码没有任何同步机制,所以不能预测或保证特定的线程执行顺序。为了使读写操作更可预测和稳定,您应该引入适当的线程同步,如读写锁(pthread_rwlock_t)或互斥锁(pthread_mutex_t)。

加锁

//读写锁测试程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

int number = 0;

//定义一把读写锁
pthread_rwlock_t rwlock;

//写线程回调函数
void* thread_write(void* arg)
{
	int i = *(int*)arg;

	int cur;

	while (1)
	{
		//加写锁
		pthread_rwlock_wrlock(&rwlock);

		cur = number;
		cur++;
		number = cur;
		printf("[%d]-W:[%d]\n", i, cur);

		//解锁
		pthread_rwlock_unlock(&rwlock);
		usleep(400);
	}
}

//读线程回调函数
void* thread_read(void* arg)
{
	int i = *(int*)arg;
	int cur;

	while (1)
	{
		//加读锁
		pthread_rwlock_rdlock(&rwlock);

		cur = number;
		printf("[%d]-R:[%d]\n", i, cur);

		//解锁
		pthread_rwlock_unlock(&rwlock);
		usleep(400);
	}
}

int main()
{
	int n = 8;
	int i = 0;
	int arr[8];
	pthread_t thread[8];

	//读写锁初始化
	pthread_rwlock_init(&rwlock, NULL);

	//创建3个写子线程
	for (i = 0; i < 3; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_write, &arr[i]);
	}

	//创建5个读子线程
	for (i = 3; i < n; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_read, &arr[i]);
	}

	//回收子线程
	int j = 0;
	for (j = 0; j < n; j++)
	{
		pthread_join(thread[j], NULL);
	}

	//释放锁
	pthread_rwlock_destroy(&rwlock);

	return 0;
}

//读写锁测试程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

int number = 0;

//定义一把读写锁
pthread_rwlock_t rwlock;

//写线程回调函数
void *thread_write(void *arg)
{
	int i = *(int *)arg;

	int cur;

	while(1)
	{
		//加写锁
		pthread_rwlock_wrlock(&rwlock);

		cur = number;
		cur++;
		number = cur;	
		printf("[%d]-W:[%d]\n", i, cur);

		//解锁
		pthread_rwlock_unlock(&rwlock);
		sleep(rand()%3);
	}
}

//读线程回调函数
void *thread_read(void *arg)
{
	int i = *(int *)arg;
	int cur;

	while(1)
	{
		//加读锁
		pthread_rwlock_rdlock(&rwlock);

		cur = number;
		printf("[%d]-R:[%d]\n", i, cur);

		//解锁
		pthread_rwlock_unlock(&rwlock);
		sleep(rand()%3);
	}	
}

int main()
{
	int n = 8;
	int i = 0;
	int arr[8];
	pthread_t thread[8];

	//读写锁初始化
	pthread_rwlock_init(&rwlock, NULL);

	//创建3个写子线程
	for(i=0; i<3; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_write, &arr[i]);
	}

	//创建5个读子线程
	for(i=3; i<n; i++)
	{
		arr[i] = i;
		pthread_create(&thread[i], NULL, thread_read, &arr[i]);
	}

	//回收子线程
	int j = 0;
	for(j=0;j<n; j++)
	{
		pthread_join(thread[j], NULL);
	}

	//释放锁
	pthread_rwlock_destroy(&rwlock);

	return 0;
}

3.条件变量

  • 条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

 ▶使用互斥量保护共享数据;

 ▶使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞.

  • 条件变量的两个动作:

 ▶条件不满足, 阻塞线程

 ▶条件满足, 通知阻塞的线程解除阻塞, 开始工作.

  • 条件变量相关函数

 ▶pthread_cond_t cond;

  ▷定义一个条件变量

 ▶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);

  ▷函数描述:销毁条件变量

  ▷函数参数:条件变量

  ▷返回值: 成功返回0,失败返回错误号

 ▶int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

  ▷函数描述: 条件不满足,引起线程阻塞并解锁;

  ▷条件满足,解除线程阻塞,并加锁

  ▷函数参数:

   cond: 条件变量

   mutex: 互斥锁变量

  ▷函数返回值: 成功返回0, 失败返回错误号

 ▶int pthread_cond_signal(pthread_cond_t *cond);

  ▷函数描述: 唤醒至少一个阻塞在该条件变量上的线程

  ▷函数参数: 条件变量

  ▷函数返回值: 成功返回0,失败返回错误号

  • 使用条件变量的代码片段

上述代码中,生产者线程调用pthread_cond_signal函数会使消费者线程在pthread_cond_wait处解除阻塞。

//使用条件变量实现生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
	int data;
	struct node* next;
}NODE;

NODE* head = NULL;

//生产者线程
void* producer(void* arg)
{
	NODE* pNode = NULL;
	while (1)
	{
		//生产一个节点
		pNode = (NODE*)malloc(sizeof(NODE));
		if (pNode == NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		pNode->data = rand() % 1000;
		printf("P:[%d]\n", pNode->data);

		pNode->next = head;
		head = pNode;

		sleep(rand() % 3);
	}
}


//消费者线程
void* consumer(void* arg)
{
	NODE* pNode = NULL;
	while (1)
	{
		printf("C:[%d]\n", head->data);
		pNode = head;
		head = head->next;
		free(pNode);
		pNode = NULL;

		sleep(rand() % 3);
	}
}

int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;

	ret = pthread_create(&thread1, NULL, producer, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	ret = pthread_create(&thread2, NULL, consumer, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//等待线程结束
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);

	return 0;
}

//使用条件变量实现生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
	int data;
	struct node* next;
}NODE;

NODE* head = NULL;

//定义一把锁
pthread_mutex_t mutex;

//定义条件变量
pthread_cond_t cond;

//生产者线程
void* producer(void* arg)
{
	NODE* pNode = NULL;
	while (1)
	{
		//生产一个节点
		pNode = (NODE*)malloc(sizeof(NODE));
		if (pNode == NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		pNode->data = rand() % 1000;
		printf("P:[%d]\n", pNode->data);

		//加锁
		pthread_mutex_lock(&mutex);

		pNode->next = head;
		head = pNode;

		//解锁
		pthread_mutex_unlock(&mutex);

		//通知消费者线程解除阻塞
		pthread_cond_signal(&cond);

		sleep(rand() % 3);
	}
}


//消费者线程
void* consumer(void* arg)
{
	NODE* pNode = NULL;
	while (1)
	{
		//加锁
		pthread_mutex_lock(&mutex);

		if (head == NULL)
		{
			//若条件不满足,需要阻塞等待
			//若条件不满足,则阻塞等待并解锁;
			//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁 
			pthread_cond_wait(&cond, &mutex);
		}

		printf("C:[%d]\n", head->data);
		pNode = head;
		head = head->next;

		//解锁
		pthread_mutex_unlock(&mutex);

		free(pNode);
		pNode = NULL;

		sleep(rand() % 3);
	}
}

int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;

	//初始化互斥锁
	pthread_mutex_init(&mutex, NULL);

	//条件变量初始化
	pthread_cond_init(&cond, NULL);

	//创建生产者线程
	ret = pthread_create(&thread1, NULL, producer, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//创建消费者线程
	ret = pthread_create(&thread2, NULL, consumer, NULL);
	if (ret != 0)
	{
		printf("pthread_create error, [%s]\n", strerror(ret));
		return -1;
	}

	//等待线程结束
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);

	//释放互斥锁
	pthread_mutex_destroy(&mutex);

	//释放条件变量
	pthread_cond_destroy(&cond);

	return 0;
}