使用文件锁进行进程间同步
文件锁的作用主要是控制多个进程间对同一个文件进行读写并发、数据读写完整性的控制。
假如有这样的一个问题,看是否可以使用文件锁进行解决。问题可以这样来描述,有N个进程需要同时处理M个目录下面的文件,要求将M个目录分配给N个进程实例,然后每个进程可以处理单个目录下面的所有文件。那么,如何让N个进程自行分配M个目录呢?
使用文件锁的总体思路
可以使用文件锁来达到任务自动分配的目的。整体思路是这样子的
- 在M个目录同级创建一个共享文件f,文件f中存储了这M个目录的所有信息。
- N个进程通过文件锁的方式去争抢文件f,当争抢到后从f中取出一个目录信息,从文件中删除该目录,然后释放文件锁,去处理目录下面的文件。
- 重复步骤2,直接文件f里的所有任务都分配完毕,文件f大小为0。
- 删除文件f。
在第一步中,文件f的创建也是由N个进程通过争抢方式来创建的,
- 如果进程1发现文件f不存在,那么尝试去创建文件f。创建文件可能失败,表明其他进程已经创建,忽略错误即可,继续向下运行,尝试去获得文件锁,等待文件f内容填充完毕后争抢任务;
- 如果进程1发现文件f已经存在,那么说明其他进程已经创建文件f,并向文件中写入所有目录的信息。则进程1尝试去获得文件锁,等待文件f内容填充完毕后争抢任务;
一旦文件f确实创建了,并且保存了所有要分配的M个目录信息,那么接下来,N个进程就可以使用文件锁的方式争抢M个目录了。直到所有目录都争抢完毕,任务处理完成后,某个进程将文件f删除了即可。
问题1:中间文件f的管理
在实际的生产环境中,中间文件f是要进行管理的,即创建、使用完成之后需要删除。其中,最关键的是删除操作。文件的删除操作需要考虑如下几个场景:
- 正常使用完成后,需要删除此文件
- 在事务commit/abort两情况下,需要删除此文件
- 进程崩溃掉之后,需要删除此文件
在上面三种场景下,处理删除文件的策略各有不同,第一种情况是非异常情况可以不用太关心。第二种情况下,事务回滚情况下,文件的删除要依赖于事务回滚机制进行,否则就需要完善事务回滚的机制了。第三种情况中,可以在进程重启过程中处理这个问题。
问题2:设计方案的缺陷
使用文件锁处理任务分配这类问题,如果不考虑性能的话,从功能完善上讲需要慎重考虑由于文件f删除而带来的问题。简单来讲,该方案使用文件锁的一个基本前提条件是, N个进程可以同时探测到共享文件f的存在。这个前提条件一旦破坏,那么设计方案就会有严重的问题。考虑一个极端的情况,描述如下(假设总共有3个进程):
- 进程1探测到文件f不存在,创建文件,填充目录信息(M=1),处理目录下的文件,然后把文件f删除了;
- 进程2此时才开始处理,同样探测到文件f不存在,所以将进程1的事情重复做了一次;
- 进程3此时才开始处理,同样探测到文件f不存在,所以将进程2的事情重复做了一次;
可以看到,在极端情况下,任务分配会出现重复N次的问题。其实,中间文件f的删除引入的问题不止如此,在实际的编码过程中,对于文件读写IO的返回值时刻要关注“文件可能被删除的问题”。那么,从问题的界定来讲,就会引入另一个问题:文件IO的错误是由于预期中的删除操作引起的,还是由于磁盘IO本身确定发生了错误。这就得再增加额外的手段,先确定是否发生了IO问题。
小结
文件锁应用于进程间同步时,文件的删除操作会引入许多问题,其中一个问题是致命的、无解的。所以,文件锁并不适用于这种场景的解决方案。可以采取两种方法来解决:
- 使用持久化的文件,不对文件进行删除
- 使用替代方案,避免引入中间文件操作