<?php
/**
* Filefifo.php 文件型FIFO队列
*/
class Filefifo
{
/**
* $_file_data, 数据文件的路径
*/
private $_file_data = ''; /**
* $_file_idx, 索引文件的路径
*/
private $_file_idx = ''; /**
* $_file_idx_bak, 索引备份文件的路径, 防止意外断电等导致索引文件破坏
*/
private $_file_idx_bak = ''; /**
* $_f_data, 数据文件的句柄
*/
private $_f_data; /**
* $_f_idx, 索引文件句柄
*/
private $_f_idx; /**
* $_f_idx_bak, 索引备份文件句柄
*/
private $_f_idx_bak; private static $_instance = array(); public static function instance($file)
{
if (! isset(self::$_instance[$file])) {
self::$_instance[$file] = new self($file);
}
return self::$_instance[$file];
} public function __construct($file)
{
$this->attach($file);
} public function __destruct()
{
$this->detach();
} /**
* attach, 挂接一个队列文件
*/
public function attach($file)
{
/**
* 初始化文件
*/
$this->_file_data = $file;
$this->_file_idx = "{$file}.idx";
$this->_file_idx_bak = "{$file}.idx.bak"; if (! file_exists($file)) {
$f = fopen($file, 'w+');
fclose($f); if (file_exists($this->_file_idx)) unlink($this->_file_idx);
if (file_exists($this->_file_idx_bak)) unlink($this->_file_idx_bak);
} $idx_data_bak = ''; /**
* 有备份则读取备份数据,无备份则创建空备份文件
*/
if (file_exists($this->_file_idx_bak)) {
$idx_data_bak = file_get_contents($this->_file_idx_bak);
} else {
$f = fopen($this->_file_idx_bak, 'w+');
fclose($f);
} /**
* 不存在索引文件则创建,并从索引备份中恢复
*/
if (! file_exists($this->_file_idx)) {
$f = fopen($this->_file_idx, 'w+');
if ($idx_data_bak) fwrite($f, $idx_data_bak);
fclose($f);
} else {
if (! file_get_contents($this->_file_idx) && $idx_data_bak) {
file_put_contents($this->_file_idx, $idx_data_bak);
}
} $this->_f_data = fopen($this->_file_data, 'a+b');
$this->_f_idx = fopen($this->_file_idx, 'rw+b');
$this->_f_idx_bak = fopen($this->_file_idx_bak, 'rw+b');
} /**
* detach, 分离当前队列文件
*/
private function detach()
{
if ($this->_f_data) fclose($this->_f_data);
if ($this->_f_idx) fclose($this->_f_idx);
if ($this->_f_idx_bak) fclose($this->_f_idx_bak);
$this->_f_data = NULL;
$this->_f_idx = NULL;
$this->_f_idx_bak = NULL;
} /**
* rewind, 设置到队列头
*/
public function rewind()
{
flock($this->_f_idx, LOCK_EX);
ftruncate($this->_f_idx, 0);
ftruncate($this->_f_idx_bak, 0);
flock($this->_f_idx, LOCK_UN);
} /**
* end, 设置到队列尾
*/
public function end()
{
flock($this->_f_idx, LOCK_EX);
// 重新计算数据文件行数
$line = $this->len();
$file_len = filesize($this->_file_data);
fseek($this->_f_data, $file_len); ftruncate($this->_f_idx, 0);
rewind($this->_f_idx);
fwrite($this->_f_idx, $file_len.",".$line); ftruncate($this->_f_idx_bak, 0);
rewind($this->_f_idx_bak);
fwrite($this->_f_idx_bak, $file_len.",".$line); flock($this->_f_idx, LOCK_UN);
} /**
* pos, 获取当前队列位置
*/
public function pos()
{
flock($this->_f_idx, LOCK_EX);
rewind($this->_f_idx);
$data_idx = fgets($this->_f_idx, 1024);
$data_idx = explode(",", $data_idx);
$pos = (int) trim($data_idx[0]);
$line = isset($data_idx[1]) ? (int) trim($data_idx[1]) : 0;
flock($this->_f_idx, LOCK_UN); return array('pos' => $pos, 'line' => $line); } /**
* len, 获取队列总长度
*/
public function len()
{
flock($this->_f_data, LOCK_EX); $old_pos = ftell($this->_f_data);
rewind($this->_f_data);
$line = 0;
while (fgets($this->_f_data, 1024) !== FALSE) $line ++;
fseek($this->_f_data, $old_pos); flock($this->_f_data, LOCK_UN); return $line;
} /**
* pop, 先进先出顺序弹出多条记录
*
* @param int $num, 一次性返回多条记录
* @param array $cur_pos, 返回当前记录所在偏移量、文件行位置信息
* @return array | boolean, 返回字符串数组记录,失败则返回FALSE
*/
public function pop($num = 1, & $cur_pos = array())
{
$num = $num < 1 ? 1 : $num; /**
* 锁定索引文件,读取索引内容
*/
flock($this->_f_idx, LOCK_EX);
rewind($this->_f_idx);
$data_idx = fgets($this->_f_idx, 1024);
$data_idx = explode(",", $data_idx);
$pos = (int) trim($data_idx[0]);
$line = isset($data_idx[1]) ? (int) trim($data_idx[1]) : 0; $data_all = array();
for ($i = 0; $i < $num; $i ++) {
/**
* 根据索引位置,读取数据文件
*/
fseek($this->_f_data, $pos);
$data = fgets($this->_f_data, 8192); /**
* 如果读取成功则更新索引记录
*/
if ($data !== FALSE) {
$pos = ftell($this->_f_data);
$line ++; rewind($this->_f_idx);
ftruncate($this->_f_idx, 0);
fwrite($this->_f_idx, "{$pos},{$line}"); rewind($this->_f_idx_bak);
ftruncate($this->_f_idx_bak, 0);
fwrite($this->_f_idx_bak, "{$pos},{$line}");
} else {
break;
} $data_all[$line] = $data;
} flock($this->_f_idx, LOCK_UN); $cur_pos = array(
'pos' => $pos,
'line' => $line,
); return $data_all ? $data_all : FALSE;
} /**
* push, 队尾压入多条记录
*
* @param string | array $data, 字符串数据,不能包含回车换行,否则会追加多条记录
* @return int, 返回插入的记录条数
*/
public function push($data)
{
if (! is_array($data)) {
$data = array($data);
} $count = 0; /**
* 锁定数据文件,追加记录
*/
flock($this->_f_data, LOCK_EX);
if (is_array($data)) {
foreach ($data as $line) {
fwrite($this->_f_data, $line."\r\n");
$count ++;
}
}
flock($this->_f_data, LOCK_UN); return $count; } /**
* del, 清空一个队列
*/
public function del()
{
$this->detach();
unlink($this->_file_data);
unlink($this->_file_idx);
unlink($this->_file_idx_bak); return TRUE;
}
}
使用文本文件作为FIFO队列,支持多进程操作同一文件,支持现场恢复。适合处理QQ用户包文本等按行分割的文件。实测每秒入队14万行,出队1万行。

主要操作

初始化一个队列
$fifo = Filefifo::instance(‘文件路径’);

出队
$data = $fifo->pop(‘要出队的行数,默认1’);

入队
$fifo->push(‘要入队的数据’)

其他操作

挂接一个数据文件
$fifo->attach(‘文件路径’)

分离当前队列文件
$fifo->detach()

移动到队列头
$fifo->rewind()

到队列尾
$fifo->end()

获取当前位置
$fifo->pos();

获取队列总长度(文件总行数)
$fifo->len()

删除队列
$fifo->del();

demo:

<?php
$file = ‘qq.txt’;
$list = Filefifo::instance($file);
$start = microtime(TRUE); // push
for ($i = 0; $i < 1000; $i ++) {
$list->push($i);
}
// pop
do {
$data = $list->pop();
} while ($data !== FALSE); echo (microtime(TRUE) - $start ) * 1000; ?>

文件 FIFO队列的更多相关文章

  1. FIFO队列算法的C程序实现

    头文件:Queue.h #ifndef _Queue_H #define _Queue_H typedef struct QueueDef_ //队列对象定义 { u16 front; //队列头部 ...

  2. C#内存映射文件消息队列实战演练(MMF—MQ)

    一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的一部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理和 ...

  3. zookeeper应用 - FIFO 队列 分布式队列

    使用ZooKeeper实现的FIFO队列,这个队列是分布式的. package fifo; import java.util.Collections; import java.util.List; i ...

  4. STL队列 之FIFO队列(queue)、优先队列(priority_queue)、双端队列(deque)

    1.FIFO队列   std::queue就是普通意思上的FIFO队列在STL中的模版. 1.1主要的方法有: (1)T front():访问队列的对头元素,并不删除对头元素 (2)T back(): ...

  5. FIFO队列(First In First Out)和优先队列

    queue<类型名> q; q.size() - 返回队列中元素个数 q.empty() - 若队列为空,返回true ,否则返回false q.pop() - 删除队首元素,但不返回其值 ...

  6. FIFO队列 ADT接口 数组实现

    FIFO.h (接口) #include "Item.h" #include <stdlib.h> typedef struct STACKnode *link; st ...

  7. Tensorflow读取文件到队列文件

    TensorFlow读取二进制文件数据到队列 2016-11-03 09:30:00      0个评论    来源:diligent_321的博客   收藏   我要投稿 TensorFlow是一种 ...

  8. redis怎么实现FIFO队列思想

    队列(FIFO)通过插入和弹出不同方向操作就可以实现,栈(FILO)插入和弹出相同方向的操作就可以实现:

  9. UOJ222 NOI2016 区间 线段树+FIFO队列

    首先将区间按长度排序后离散化端点(这里的“长度”指的是离散化之前区间的实际长度) 然后模拟一个队列,区间按排好的顺序依次进入,直到某个点被覆盖了M次.之后依次出队,直到所有点都被覆盖小于M次 修改和询 ...

随机推荐

  1. python 字符串 大小写转换

    总结 capitalize() 首字母大写,其余全部小写 upper() 全转换成大写 lower() 全转换成小写 title() 标题首字大写,如"i love python" ...

  2. E:nth-child(n)实现奇偶匹配

    <style> li:nth-child(2n){color:#f00;} /* 偶数 */ li:nth-child(2n+1){color:#000;} /* 奇数 */ </s ...

  3. Applied Deep Learning Resources

    Applied Deep Learning Resources A collection of research articles, blog posts, slides and code snipp ...

  4. SoftmaxLayer and SoftmaxwithLossLayer 代码解读

    SoftmaxLayer and SoftmaxwithLossLayer 代码解读 Wang Xiao 先来看看 SoftmaxWithLoss 在prototext文件中的定义: layer { ...

  5. 胶囊碰撞体(CapsuleCollider)

    胶囊碰撞体 (Capsule Collider) 胶囊碰撞体 (Capsule Collider) 由两个半球体与一个圆柱体相连接而构成.它与胶囊 (Capsule) 基元形状相同.   一堆胶囊碰撞 ...

  6. Postfix 发送邮件流程简析

      PostFix接受和转发邮件的说明 来源ip符合inet_interfaces,收件人域符合mydestination, Postfix将接收到本地. 来源ip符合inet_interfaces, ...

  7. PDA通过SocketTcp+Json和SuperSocket通信方式

    从Pda请求到服务器处理请求,并返回结果,Pda拿到结果的流程.

  8. JS动态广告浏览

    <script language="JavaScript"> function addEventSimple(obj,evt,fn){ if(obj.addEventL ...

  9. Android Afinal框架(二)

    框架地址:https://github.com/yangfuhai/afinal 对应的源码: net.tsz.afinal.annotation.view.* FinalActivity Final ...

  10. 解决JQuery中datatables设置隐藏显示列多次提交后台刷新数据的问题

    此次项目开发过程中用到了Jquery的Datatables插件,无疑他是数据列表展示,解决MVC中同步过程中先走控制器后返回视图,查询数据过程中无法提示等待的弊端, 而且他所提供的各种方法也都有较强的 ...