系统编程练习题----利用条件量和互斥锁,实现两个线程之间的同步与互斥
题目
解析
该题主要依靠条件量和互斥锁来实现线程之间的同步与互斥,分析主线程、线程A和线程B的任务如下:
主线程:
- 打开LCD屏和触摸屏的硬件文件,并分别存储两个文件的文件描述符,方便后面进行条件判断。
- 开启线程A和线程B。
- 定义并初始化条件量和互斥量,方便后续线程内进行上锁和挂起步骤
线程A:
- 读取触摸屏坐标,并对读取到的坐标值进行判断。
- 坐标条件判断满足后,利用pthread_cond_signal()函数接口对线程B中挂起的条件量进行唤醒操作。
线程B:
- 当flag条件不满足时,进入挂起状态,等待线程A的唤醒,再次对flag条件进行判断。
- 当flag条件满足且自身处于唤醒状态时,输出相应的字符串到终端。
代码
/********************************************************************************
*
* file name: demo.c
* author : ProgramMonkey_Fly@126.com
* date : 2024/05/31
* function : 该案例是掌握条件量与互斥锁联合使用过程,
* 在进程中使用条件量和互斥锁实现线程的同步以及临界资源的互斥访问
* note :
* 由于使用了线程函数接口,所以编译时需要加上-pthread
* version :
*
* CopyRight (c) 2023-2024 ProgramMonkey_Fly@126.com All Right Reseverd
*
* ******************************************************************************/
/****************************头文件*********************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/input.h> //触摸屏
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
/****************************全局变量*********************************************/
volatile int flag; //临界资源,用于条件判断是否满足输出字符串
int *lcd_mp; //指向LCD屏映射空间地址的指针变量
int ts_fd; //触摸屏文件
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; //定义一个互斥量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //定义一个条件量
// #define DEBUG //调试代码用,开启后可看到触摸坐标
/********************************************************************************
*
* name : thread_A_task
* function : 线程A的任务函数,实现对触摸屏坐标的判断以及条件唤醒线程B
* param :
* none
*
* retval : none
* author : ProgramMonkey_Fly@126.com
* date : 2024/05/31
* note : 由于线程B处于挂起状态,所以一定要在线程A中设定条件对线程B进行唤醒,防止
* 死锁情况出现
* version :
*
* *****************************************************************************/
void *thread_A_task(void *arg)
{
//定义触摸屏参数变量
struct input_event ts_event;
//定义一个变量,用于判断坐标是否读取完整
int cnt = 0;
//定义两个变量,分别存触摸屏横坐标与纵坐标
int x = -1, y = -1;
//循环判断触摸屏坐标是否满足“处于左上角”条件
while(1)
{
read(ts_fd, &ts_event, sizeof(ts_event));
//3.分析读取的设备信息 (type + code + value)
if (ts_event.type == EV_ABS) //说明是触摸屏
{
if (ts_event.code == ABS_X) //说明是X轴
{
cnt++;
x = ts_event.value * 800 / 1024;
}
if (ts_event.code == ABS_Y) //说明是Y轴
{
cnt++;
y = ts_event.value * 480 / 600;
}
}
//读取完整的坐标进行判断
if(cnt >= 2)
{
//计数器清零
cnt = 0;
#ifdef DEBUG
//调试代码用
printf("x = %d y = %d\n",x,y);
#endif
//上锁
pthread_mutex_lock(&fastmutex);
//条件判断
if(x >= 0 && x <= 200 && y >= 0 && y <= 200 )
{
//清空获取坐标
x = -1;
y = -1;
//将标志量置为一
flag = 1;
// 条件满足时唤醒线程B
pthread_cond_signal(&cond);
}
// 解锁
pthread_mutex_unlock(&fastmutex);
}
}
}
/********************************************************************************
*
* name : thread_B_task
* function : 线程B的任务函数,在flag小于0时挂起,等待线程A的唤醒;在唤醒状态下且条件
* 满足时,在终端输出一串字符串
* param :
* none
*
* retval : none
* author : ProgramMonkey_Fly@126.com
* date : 2024/05/31
* note : 由于线程B处于挂起状态,所以一定要在线程A中设定条件对线程B进行唤醒,防止
* 死锁情况出现
* version :
*
* *****************************************************************************/
void *thread_B_task(void *arg)
{
//循环判断输出字符串条件是否满足
while(1)
{
//上锁
pthread_mutex_lock(&fastmutex);
while(!flag)
{
pthread_cond_wait(&cond, &fastmutex);
}
printf("Touch coordinates in the upper left corner~\n");
flag = 0;
// 解锁
pthread_mutex_unlock(&fastmutex);
}
}
int main(int argc,const char *argv[])
{
/****条件准备****/
// 打开LCD
int lcd_fd = open("/dev/fb0", O_RDWR);
if(lcd_fd == -1)
{
fprintf(stderr, "open error, errno : %d, %s\n", errno, strerror(errno));
exit(1);
}
// 对LCD进行内存映射 mmap
lcd_mp = (int *)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(lcd_mp == MAP_FAILED)
{
fprintf(stderr, "mmap error, errno : %d, %s\n", errno, strerror(errno));
exit(2);
}
// 打开触摸屏
ts_fd = open("/dev/input/event0", O_RDWR);
if(ts_fd == -1)
{
fprintf(stderr, "open error, errno : %d, %s\n", errno, strerror(errno));
exit(3);
}
//初始化一个互斥锁
pthread_mutex_init(&fastmutex, NULL);
//初始化一个条件量
pthread_cond_init(&cond, NULL);
//创建两个线程:线程A和线程B
pthread_t thread_A;
pthread_t thread_B;
pthread_create(&thread_A, NULL, thread_A_task, NULL);
pthread_create(&thread_B, NULL, thread_B_task, NULL);
//将线程A设定为分离模式
pthread_detach(thread_A);
pthread_detach(thread_B);
//关闭主线程
pthread_exit(NULL);
}