前言:通过一个任务导入并分析线程同步的信号量。
1、任务:用户从终端输入任意字符,然后统计字符个数并显示,输入end则结束。
2、分析:
使用多线程实现:主线程获取用户输入并判断是否退出,子线程计数。
3、信号量的使用及编程实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
char buf[200] = {0};
sem_t sem; //定义一个信号量
//子线程程序,作用是统计buf中的字符个数并打印
void * func(void * arg)
{
//子线程首先应该有个循环
//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,
//然后打印;
//完成打印后,再次被阻塞
sem_wait(&sem); //阻塞
while(strncmp(buf, "end", 3) != 0)
{
printf("本次输入了%d个字符\n", (int)strlen(buf));
memset(buf, 0, sizeof(buf));
sem_wait(&sem);
}
pthread_exit(NULL); //子线程退出
}
int main(void)
{
int ret = -1;
pthread_t th = -1;
sem_init(&sem, 0, 0); //初始化信号量
ret = pthread_create(&th, NULL, func, NULL); //创建子线程
if (ret != 0)
{
printf("pthread_create error.\n");
exit(-1);
}
printf("输入一个字符串,以回车结束\n");
while(scanf("%s", buf))
{
//去比较用户输入的是不是end,如果是则退出,如果不是则继续
if (!strncmp(buf, "end", 3))
{
sem_post(&sem); //抛出信号量
printf("程序结束\n");
//exit(0);
break;
}
//主线程在收到用户输入的字符串,并且确认不是end后
//就去发信号激活子线程来计数。
//子线程被阻塞,主线程可以激活,这就是线程的同步问题。
//信号量就可以用来实现这个线程同步
sem_post(&sem); //抛出信号量
}
printf("等待回收子线程\n");
ret = pthread_join(th, NULL); //回收子线程
if (ret != 0)
{
printf("pthread_join error.\n");
exit(-1);
}
printf("子线程回收成功\n");
sem_destroy(&sem); //销毁信号量
return 0;
}
4、线程同步之互斥锁
4.1、什么是互斥锁
(1)互斥锁又叫互斥量(mutex)
(2)相关函数:
- pthread_mutex_init
- pthread_mutex_destroy
- pthread_mutex_lock
- pthread_mutex_unlock
(3)互斥锁和信号量的关系:可以认为互斥锁是一种特殊的信号量
(4)互斥锁主要用来实现关键代码段保护
4.2、用互斥锁来实现关键段代码保护
注意:在编写程序,查询pthread_mutex_init 先关函数时,如:
man 3 pthread_mutex_init 时提示找不到函数,说明你当前linux系统环境没有安装pthread相关的man手册。
安装方法:
(1)虚拟机能上网;
(2)使用安装命令: sudo apt-get install manpages-posix-dev
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
char buf[200] = {0};
pthread_mutex_t mutex; //定义一个信号量
unsigned char flag = 0; //标志位
//子线程程序,作用是统计buf中的字符个数并打印
void * func(void * arg)
{
//子线程首先应该有个循环
//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,
//然后打印;
//完成打印后,再次被阻塞
sleep(1);
while(flag == 0)
{
pthread_mutex_lock(&mutex); //上锁
printf("本次输入了%d个字符\n", (int)strlen(buf));
memset(buf, 0, sizeof(buf));
pthread_mutex_unlock(&mutex); //解锁
sleep(1);
}
pthread_exit(NULL); //线程退出
}
int main(void)
{
int ret = -1;
pthread_t th = -1;
pthread_mutex_init(&mutex, NULL); //初始化信号量
ret = pthread_create(&th, NULL, func, NULL); //创建子线程
if (ret != 0)
{
printf("pthread_create error.\n");
exit(-1);
}
printf("输入一个字符串,以回车结束\n");
while(1)
{
pthread_mutex_lock(&mutex); //上锁
scanf("%s", buf);
pthread_mutex_unlock(&mutex); //解锁
//去比较用户输入的是不是end,如果是则退出,如果不是则继续
if (!strncmp(buf, "end", 3))
{
printf("程序结束\n");
flag = 1;
//exit(0);
break;
}
sleep(1);
}
printf("等待回收子线程\n");
ret = pthread_join(th, NULL); //回收子线程
if (ret != 0)
{
printf("pthread_join error.\n");
exit(-1);
}
printf("子线程回收成功\n");
pthread_mutex_destroy(&mutex); //销毁信号量
return 0;
}
注:这个程序实例仅为了学习如何使用互斥锁来实现关键段代码保护,知晓大概流程及实现机理,实用意义不大。
5、线程同步之条件变量
5.1、相关函数
- pthread_cond_init
- pthread_cond_destroy
- pthread_cond_wait
- pthread_cond_signal
- pthread_cond_broadcast
5.2、使用条件变量来实现4.2中的代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
char buf[200] = {0};
pthread_mutex_t mutex; //定义一个信号量
pthread_cond_t cond; //定义一个条件变量
unsigned char flag = 0; //定义一个标志位
//子线程程序,作用是统计buf中的字符个数并打印
void * func(void * arg)
{
//子线程首先应该有个循环
//循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符长度,
//然后打印;
//完成打印后,再次被阻塞
while(flag == 0)
{
pthread_mutex_lock(&mutex); //上锁
pthread_cond_wait(&cond, &mutex); //阻塞在这里,等待条件信号到来
printf("本次输入了%d个字符\n", (int)strlen(buf));
memset(buf, 0, sizeof(buf));
pthread_mutex_unlock(&mutex); //解锁
}
pthread_exit(NULL); //线程退出
}
int main(void)
{
int ret = -1;
pthread_t th = -1;
pthread_mutex_init(&mutex, NULL); //初始化信号量
pthread_cond_init(&cond, NULL); //条件变量初始化
ret = pthread_create(&th, NULL, func, NULL); //创建子线程
if (ret != 0)
{
printf("pthread_create error.\n");
exit(-1);
}
printf("输入一个字符串,以回车结束\n");
while(1)
{
scanf("%s", buf);
pthread_cond_signal(&cond); //发送条件变量信号
//去比较用户输入的是不是end,如果是则退出,如果不是则继续
if (!strncmp(buf, "end", 3))
{
printf("程序结束\n");
flag = 1;
break;
}
}
printf("等待回收子线程\n");
ret = pthread_join(th, NULL); //回收子线程
if (ret != 0)
{
printf("pthread_join error.\n");
exit(-1);
}
printf("子线程回收成功\n");
pthread_mutex_destroy(&mutex); //销毁信号量
pthread_cond_destroy(&cond); //销毁条件变量信号
return 0;
}