最近看了一下线程的同步知识,研究起来还是linux下的线程好研究,关于线程同步一般有以下几种方法:信号量、互斥锁、读写锁、条件变量,单独看互斥锁是比较明白的,一开始一直没搞明白互斥锁+条件变量一起使用的时候,因为这个组合感觉用到的最多,互斥锁主要是解决线程竞争所使用的手段,而条件变量书中说到是线程交互的场所,提供了一种同步机制,而由于条件变量是线程都要访问的东西,因此是需要互斥锁保护的,先单独看下使用互斥锁的情况。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
void *thread_function1(void* arg);
void *thread_function2(void* arg);
struct apple
{
int t_number;
pthread_mutex_t t_mutex;
};
struct apple * apple_init()
{
struct apple *apple;
int res;
apple = malloc(sizeof(struct apple));
apple->t_number = 10;
res = pthread_mutex_init(&apple->t_mutex,NULL);
if(res != 0)
{
perror("mutex init failed");
free(apple);
}
return apple;
}
struct apple *apple;
int main()
{
int res;
pthread_t thread1,thread2;
void *thread_result;
apple = apple_init();
res = pthread_create(&thread1,NULL,thread_function1,NULL);
if(res != 0)
{
perror("Thread1 create failed");
exit(0);
}
printf("thread1 create success...\n");
res = pthread_create(&thread2,NULL,thread_function2,NULL);
if(res != 0)
{
perror("Thread2 create failed");
exit(0);
}
printf("thread2 create success...\n");
res = pthread_join(thread2,&thread_result);
if(res != 0)
{
perror("Thread2 join failed");
exit(0);
}
printf("Thread2 joined.it returned: %s\n",(char*)thread_result);
sleep(2);
pthread_mutex_destrpy(&apple->t_mutex);
free(apple);
exit(1);
}
void *thread_function1(void *arg)
{
while(1)
{
pthread_mutex_lock(&apple->t_mutex);
printf("there are %dapple left\n", apple->t_number);
if(apple->t_number == 0)
{
pthread_mutex_unlock(&apple->t_mutex);
pthread_exit("no apples");
}
pthread_mutex_unlock(&apple->t_mutex);
sleep(1);
}
}
void *thread_function2(void *arg)
{
while(1)
{
pthread_mutex_lock(&apple->t_mutex);
apple->t_number--;
if(apple->t_number == 0)
{
pthread_mutex_unlock(&apple->t_mutex);
pthread_exit("apple sold out!");
}
pthread_mutex_unlock(&apple->t_mutex);
sleep(1);
}
}
这个代码很简单开启了两个线程,一个线程是减少苹果数量,一个线程是读取苹果数量,两者都用互斥锁来防止竞争,在解锁之后有一个休眠1s的操作,运行的结果如下所示:
以上运行结果在每次运行时都是不同的,但并不影响我们的结果,在该例中我们的注重点是两个线程都需要使用互斥锁来保护我们所需要访问的变量即苹果的数量,防止线程2在修改该变量时,线程1去读取该变量的值,虽然这种情况我们可以使用读写锁更好。那条件变量什么时候需要呢?
条件变量一定是需要某个条件发生时,且平时一直在等待该信号的发生,使线程处于阻塞状态,在线程1中,我们读取到苹果的数量为0了就退出线程1,现在修改一下逻辑,线程2就好比顾客,线程1就好比商店,当顾客把商店的苹果买完了之后,商店就关门,在线程1种我们就等待一个条件变量,一直处于阻塞状态,在线程2中我们唤醒该条件变量,告诉商店,苹果已经卖完了。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
void *thread_function1(void* arg);
void *thread_function2(void* arg);
struct apple
{
int t_number;
pthread_mutex_t t_mutex;
pthread_cond_t t_cond;
};
struct apple * apple_init()
{
struct apple *apple;
int res;
apple = malloc(sizeof(struct apple));
apple->t_number = 10;
res = pthread_mutex_init(&apple->t_mutex,NULL);
if(res != 0)
{
perror("mutex init failed");
free(apple);
}
res = pthread_cond_init(&apple->t_cond,NULL);
if(res != 0)
{
perror("cond init failed");
free(apple);
}
return apple;
}
struct apple *apple;
int main()
{
int res;
pthread_t thread1,thread2;
void *thread_result;
apple = apple_init();
res = pthread_create(&thread1,NULL,thread_function1,NULL);
if(res != 0)
{
perror("Thread1 create failed");
exit(0);
}
printf("thread1 create success...\n");
res = pthread_create(&thread2,NULL,thread_function2,NULL);
if(res != 0)
{
perror("Thread2 create failed");
exit(0);
}
printf("thread2 create success...\n");
res = pthread_join(thread2,&thread_result);
if(res != 0)
{
perror("Thread2 join failed");
exit(0);
}
printf("Thread2 joined.it returned: %s\n",(char*)thread_result);
sleep(2);
pthread_mutex_destroy(&apple->t_mutex);
pthread_cond_destroy(&apple->t_cond);
free(apple);
exit(1);
}
void *thread_function1(void *arg)
{
pthread_mutex_lock(&apple->t_mutex);
printf("there are %dapple left\n", apple->t_number);
pthread_cond_wait(&apple->t_cond,&apple->t_mutex);
pthread_mutex_unlock(&apple->t_mutex);
pthread_exit("close door");
}
void *thread_function2(void *arg)
{
while(1)
{
pthread_mutex_lock(&apple->t_mutex);
printf("buy one apple\n");
apple->t_number--;
if(apple->t_number == 0)
{
pthread_cond_signal(&apple->t_cond);
pthread_mutex_unlock(&apple->t_mutex);
pthread_exit("apple sold out!");
}
pthread_mutex_unlock(&apple->t_mutex);
sleep(1);
}
}
为结构体增加了一个pthread_cond_t变量,在线程1中去掉了无限循环,因为等待条件变量函数pthread_cond_wait是一个阻塞函数,运行结果如下:
在线程2中我们逐渐减少苹果的数量,当减到为0时就唤醒条件变量,退出线程2,线程1等到条件变量后退出线程1,其中需要注意的是pthread_cond_wait函数中是先执行了解锁操作,当接收到条件变量唤醒后又执行的上锁操作。
条件变量的使用一定是线程需要等待某一条件的发生而要阻塞在那里,如果不需要阻塞运行的话那就没有必要使用条件变量了,直接用无限循环不停地判断就好了,只是这样比较浪费cpu资源而已,就像我们前面第一个例子那样不停地判断。