多线程写同一个文件时,多个线程同时操作文件会导致写入的内容相互覆盖、顺序错乱,甚至出现数据损坏的情况,使用flock实现文件锁可以有效解决这个问题,保证同一时间只有一个线程能够写入目标文件。

flock 基本原理
flock是Linux系统提供的文件锁机制,作用于整个文件,支持两种锁类型:
- 共享锁(读锁):多个进程或线程可以同时持有共享锁,适合读操作场景,不会互相阻塞。
- 排他锁(写锁):同一时间只能有一个进程或线程持有排他锁,其他尝试获取锁的操作会被阻塞,适合写操作场景。
在多线程写文件的场景中,我们需要使用排他锁,确保写操作执行期间其他线程无法对文件进行写入。
多线程写文件使用 flock 的实现步骤
1. 打开目标文件
首先需要在每个线程中打开要写入的目标文件,获取文件描述符,后续通过这个描述符操作文件锁。
2. 获取排他锁
在写入文件内容之前,调用flock函数对文件描述符加排他锁,如果锁已经被其他线程持有,当前线程会进入阻塞状态,直到锁被释放。
3. 执行写入操作
成功获取锁之后,执行文件写入逻辑,此时其他线程无法同时写入该文件,避免内容冲突。
4. 释放锁并关闭文件
写入完成之后,调用flock释放锁,然后关闭文件描述符,避免资源泄漏。
PHP 实现示例
以下是使用PHP实现多线程写同一个文件并加flock锁的示例代码,这里使用pthreads扩展模拟多线程场景:
<?php
class WriteFileThread extends Thread {
private $filePath;
private $content;
public function __construct($filePath, $content) {
$this->filePath = $filePath;
$this->content = $content;
}
public function run() {
// 打开文件,追加模式
$fp = fopen($this->filePath, 'a');
if (!$fp) {
echo "打开文件失败n";
return;
}
// 获取排他锁,阻塞直到获取成功
if (flock($fp, LOCK_EX)) {
// 写入内容
fwrite($fp, $this->content . PHP_EOL);
// 释放锁
flock($fp, LOCK_UN);
} else {
echo "获取文件锁失败n";
}
// 关闭文件
fclose($fp);
}
}
$filePath = '/tmp/test_write.txt';
// 清空测试文件
file_put_contents($filePath, '');
// 创建10个线程写入文件
$threads = [];
for ($i = 0; $i < 10; $i++) {
$thread = new WriteFileThread($filePath, "线程{$i}写入的内容");
$thread->start();
$threads[] = $thread;
}
// 等待所有线程执行完成
foreach ($threads as $thread) {
$thread->join();
}
echo "所有线程写入完成n";
?>
Shell 脚本实现示例
如果是Shell脚本场景下的多线程写文件,也可以使用flock命令实现锁控制:
#!/bin/bash
# 目标文件路径
FILE_PATH="/tmp/test_shell_write.txt"
# 清空测试文件
> $FILE_PATH
# 定义写入函数
write_file() {
local thread_id=$1
# 使用flock加排他锁,执行写入命令
flock -x 200
echo "线程${thread_id}写入的内容" >> $FILE_PATH
# 锁会在文件描述符200关闭时自动释放
flock -u 200
}
# 打开文件,绑定到文件描述符200
exec 200> $FILE_PATH
# 启动10个后台进程模拟多线程
for i in {1..10}; do
write_file $i &
done
# 等待所有后台进程完成
wait
echo "所有写入完成"
使用注意事项
- flock锁是建议性锁,意味着如果其他线程不尝试获取锁直接写入文件,锁不会生效,所以所有写文件的线程都需要遵循加锁逻辑。
- 加锁之后要确保在异常情况下也能释放锁,避免死锁,可以在代码中加入异常处理逻辑。
- flock锁在文件描述符关闭时会自动释放,所以不需要手动释放的场景下,只要保证文件正确关闭即可。
- 不同进程之间也可以使用flock实现文件锁互斥,不仅限于多线程场景。
注意:flock是系统级文件锁,不适用于网络文件系统场景,网络文件系统下建议使用其他分布式锁方案。