本科时候所学习的操作系统课程,里面有一个非常重要的部分是关于进程的同步,考试也是必考内容,以前一直感觉晕晕的,想不到这个时候验证了“出来混总是要还的”这句话。以前没理解好,现在来恶补一下。多线程作为现代操作系统的一个基本特征,很多地方和进程的设计理念是类似的,就比如同步算法。
我们知道,当多个线程同时运行时,是由操作系统对其进行调度,线程的运行顺序是我们无法控制的。这个时候,传统的结构化程序自顶向下的分析思想就显得捉襟见肘了。而事实是,不仅在程序分析上会变得很麻烦,程序对资源的访问的控制也需要进行非常仔细的掂量。关于“读脏”数据等并发引起的问题就不进行详细叙述。
我们来看第一个非常简单的例子,非常经典的读者/写者问题。
问题简单描述:有一个读者和一个写者,当写者在进行写作的时候,读者不能阅读,读者阅读的时候,写者不能进行写作。也就是说,读者和写者只能有一个运行,另外一个要阻塞。
算法是用C语言在Linux下实现的
/** **读写互斥问题 **读的时候不能写,写的时候不能读 ** **/ #include<stdio.h> #include<pthread.h> //声明一把互斥锁 pthread_mutex_t mutex; void writer(void) { while(1) { pthread_mutex_lock(&mutex); printf("writer加锁成功,开始写...\n"); sleep(2); printf("writer写操作结束,释放互斥锁\n"); pthread_mutex_unlock(&mutex); } } void reader(void) { while(1) { pthread_mutex_lock(&mutex); printf("reader加锁成功,开始读...\n"); sleep(2); printf("reader读操作结束,释放互斥锁\n"); pthread_mutex_unlock(&mutex); } } int main(void) { printf("初始化互斥锁\n"); pthread_mutex_init(&mutex,NULL); pthread_t thread_writer; pthread_t thread_reader; printf("开始读写线程\n"); pthread_create(&thread_reader,NULL,(void *)reader,NULL); pthread_create(&thread_writer,NULL,(void *)writer,NULL); printf("回收线程\n"); pthread_join(thread_reader,NULL); pthread_join(thread_writer,NULL); printf("运行结束\n"); return 0; }
注意:编译的时候要加上-lpthread
编译命令示例:gcc -o mutex.exe mutex.c -lpthread
这个程序由于设计上不是很合理,会进入一个无限循环,所以在控制台下运行以后要使用Ctrl+C来强行结束程序的运行,不过不影响整个算法的主要流程。
我们运行以后可以发现一个很严重的问题,读者或者写者的其中一个可能会长期占据着互斥锁的加锁权,从而导致另外一个线程长期没有机会运行,导致一种线程“饿死”现象。
在实际的读写过程中,比较正常的一种简单情况是,先写再读,读写交替。下一篇文章我们来改进一下本文的算法,使其达到读写交替的目的。