1、 进程1和进程2并发读取test文件的数据:
线程(进程)的ID读写数据状态1~不变2~不变2、 进程1读取test文件的数据,进程2向test文件写入新的数据
进程(线程)的ID读写数据状态1~读取原数据(或进程2写入后数据)2~改变3、 进程2读取test文件的数据,进程1向test文件写入新的数据
进程(线程)的ID读写数据状态1~改变2~读取原数据(或进程2写入后的数据)4、 进程1和进程2向test文件写入新的数据
进程(线程)的ID读写数据状态1~改变1(可能覆写进程2修改的数据)2~改变2(可能覆写进程1的修改的数据)由此可知,多线程或多进程在并发写相同文件时,可能覆写对方的数据。若多个线程(或多个进程)在按照顺序写同一个文件时,就不会发生覆写数据的情况。
因此,多线程(或多进程)在写同一个文件时就需要给文件加一把锁,每次只能有一个线程写文件,其他线程等待当前线程写完退出并释放锁之后才能写。
很显然,文件写操作是互斥的,应该加互斥锁,即写锁和写锁是互斥的;多个线程读文件则是可以加读锁,加读锁是为了防止其他线程写文件,那么读锁和写锁也是互斥的。
两个线程同时写一个文件时:
两个线程修改文件中同一行数据,则先后顺序很重要,最后文件的结果显示最后一个线程修改后的内容。
两个线程修改文件中不同行的数据,最后文件的就是两个线程修改后的内容,既包括第一个线程的修改也包括第二个线程的修改。
每个线程修改文件内容的操作:读取-修改-写回 线程要修改的数据是由读取操作得到的文件内容。因此,读取的时刻非常重要,若在其他线程修改写会后读取则获得的是最新的内容;若在其他线程写会前读取则获得的是原文件内容。 线程读取文件、修改内容、写回分别是可看做原子操作
第一种情况,即使文件加互斥锁也不能保证多线程同时写文件的同一行数据的结果是用户想要看到的。因此必须严格保证写同一行数据线程的顺序性。修改同一块数据的操作最好放在同一个线程中,而不是多线程环境下。(此处仅讨论修改文件同一行数据的问题)
第二种情况,必须给文件加锁来保证线程写文件的顺序性。若两个线程同时读到文件原数据,分别作修改并写回文件,那么就会发生覆写其他线程的修改,其结果为最后一个写回文件的内容。因此必须通过给文件加锁来保证多个线程写文件后文件内容的一致性。
如何给文件加锁? 可参考:Linux的进程间通信-文件和文件锁
给整个文件加锁时,即使多个线程修改文件的不同行(不同部分),也必须等待其他线程访问结束。如果文件中的每一行(或文件的某个部分)可以加锁,这就可以提高多线程在修改同一文件不同行(不同部分)的效率。
很明显,给文件加锁的粒度要比给文件行(部分)加锁的粒度大,为了提高访问文件的效率,根据具体情况选择合适粒度的锁。
unix提供fcntl记录锁的机制,可以给文件部分加锁,具体使用方式请参考Unix环境高级编程第14.3节。
1 并发编程的三个概念:原子性,可见性,有序性
这是由一篇文章引发的思考和一些知识的串联。