各位看官们,大家好,上一回中咱们说的是线程同步之信号量的例子,这一回咱们继续说该例子。闲话休提,言归正转。让我们一起talk C栗子吧!
我们在上一回中详细介绍了信号量相关函数的用法,这一回中,我们介绍如何使用这些函数来操作信号量。
下面是详细的操作步骤:
- 1.定义一个信号量A,用来同步线程;
- 2.在创建线程的进程中使用sem_init函数把信号量A的值初始化为0;
- 3.在读取数据的线程中使用sem_wait实现P操作;
- 4.在第写数据的线程中修改数据,写数据的操作完成后使用sem_post实现V操作;
- 5.在创建线程的进程中使用sem_destroy函数释放信号量相关的资源;
看官们,正文中就不写代码了,详细的代码放到了我的资源中,大家可以点击这里下载使用。
在代码中我们自己实现了读/写数据的函数,为了方便说明问题,在这两个函数中都使用了延时操作,目的是为了说明读或者写数据需要一定的时间。
在程序运行时可能会存在这样的情况:
- 读操作还没有完成,就开始写操作,这样会造成读操作读取的数据不准确;
- 写操作还没有完成,就开始读操作,这样会造成读操作读取的数据不准确;
void read_data(char * str)
{
printf("[%s] start reading data \n",str);
sleep(1); //延时操作
printf("[%s] data = %d \n",str,data); //对数据进行读操作,并且显示数据
printf("[%s] end reading data \n",str);
}
void write_data(char * str)
{
printf("[%s] start writing data \n",str);
sleep(1); //延时操作
data++; //对数据进程写操作
printf("[%s] data = %d \n",str,data);
printf("[%s] end writing data \n",str);
}
下面是程序的运行结果,请大家参考:
Create first thread //创建第一个线程
Create second thread //创建第二个线程
Thread ID::3076148032 -----------S----------
[Thread_1] start reading data //第一个线程开始读取数据(对数据的第一个操作是读操作)
Thread ID::3067755328 -----------S----------
[Thread_2] start writing data //第二个线程开始写数据
[Thread_1] data = 0 //写操作还没有完成,第一个线程读取到的数据是初始值
[Thread_1] end reading data
[Thread_1] start reading data
[Thread_2] data = 1
[Thread_2] end writing data //写操作完成,第二个线程通过写操作把数据修改为1
[Thread_1] data = 1 //写操作完成,第一个线程读取到的数据是写操作修改后的数据
[Thread_1] end reading data
[Thread_1] start reading data
[Thread_2] start writing data
[Thread_1] data = 1
[Thread_1] end reading data
Thread ID::3076148032 -----------E---------- //读取数据的线程结束
[Thread_2] data = 2
[Thread_2] end writing data
[Thread_2] start writing data
[Thread_2] data = 3
[Thread_2] end writing data
[Thread_2] start writing data
[Thread_2] data = 4
[Thread_2] end writing data
Thread ID::3067755328 -----------E---------- //写数据的结束结束
大家从上面的结果中可以看到,对数据的读操作还没有完成,写操作就开始运行,或者是写操作还没有完成,读操作就开始运行。总之,读操作和写操作混合运行,程序中读操作显示的数据不准确。
我们按照上面对信号量的操作步骤,在程序中添加了信号量,通过信号量来实现线程同步。在代码中为了方便操作,只需要把宏定义代码前的注释去掉就可以:
//#define SEM_ENABLE 1
因为我们在代码中使用宏来控制信号量,详细如下:
#ifdef SEM_ENABLE
res = sem_wait(&sem_value);
if(res != 0)
{
printf(" sem wait failed \n");
}
#endif
#ifdef SEM_ENABLE
res = sem_post(&sem_value);
if(res != 0)
{
printf(" sem post failed \n");
}
#endif
下面是使用信号量同步线程后,程序的运行结果,请大家参考:
Create first thread //创建第一个线程
Create second thread //创建第二个线程
Thread ID::3075992384 -----------S----------
Thread ID::3067599680 -----------S----------
[Thread_2] start writing data //第二个线程开始写数据(对数据的第一个操作是写操作)
[Thread_2] data = 1
[Thread_2] end writing data //第二个线程中写数据的操作已经完成
[Thread_1] start reading data //第一个线程开始读取数据
[Thread_1] data = 1
[Thread_1] end reading data //第一个线程中读取数据的操作已经完成
[Thread_2] start writing data
[Thread_2] data = 2
[Thread_2] end writing data
[Thread_1] start reading data
[Thread_1] data = 2
[Thread_1] end reading data
[Thread_2] start writing data
[Thread_2] data = 3
[Thread_2] end writing data
[Thread_1] start reading data
[Thread_1] data = 3
[Thread_1] end reading data
[Thread_2] start writing data
[Thread_2] data = 4
[Thread_2] end writing data
Thread ID::3075992384 -----------E---------- //读取数据的线程结束
Thread ID::3067599680 -----------E---------- //写数据的线程结束
从上面的运行结果中可以看到,在使用信号量对两个线程进行同步操作后,读取数据的线程和写数据的线程依次运行:先写数据,然后才读数据。这样保证了读取到的数据是准确的。
另外,大家可以看到,主进程先创建读取数据的线程,然后才创建写数据的线程。因此,应该是先读取数据,然后才写数据。不过,因为我们使用信号量对线程进行了同步,程序运行时,先执行写操作,然后才执行读操作。因此,在运行结果中,对数据的第一个操作是写操作。这一点可以和没有使用信号量的程序运行结果进行对比。
各位看官,关于线程同步之信号量的例子咱们就说到这里。欲知后面还有什么例子,且听下回分解 。