<?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 study - 正则表达式

    第 7 章 正则表达式 7.1. 概览 7.2. 个案研究:街道地址 7.3. 个案研究:罗马字母 7.3.1. 校验千位数 7.3.2. 校验百位数 7.4. 使用 {n,m} 语法 7.4.1. ...

  2. spring源码学习之:spring容器的applicationContext启动过程

    Spring 容器像一台构造精妙的机器,我们通过配置文件向机器传达控制信息,机器就能够按照设定的模式进行工作.如果我们将Spring容器比喻为一辆汽车,可以将 BeanFactory看成汽车的发动机, ...

  3. Java设计模式之适配器设计模式

    1.适配器模式( Adapter)定义将一个类的接口转换成客户希望的另外一个接口.Adapter 模式使得原来由于接口不兼容而不能一起工作的 那些类可以一起工作. 现实案例如下:  墙上电源类(22 ...

  4. OpenCV CommandLineParser 的用法

    OpenCV CommandLineParser 的用法 去百度了一下,关键字:OpenCV CommandLineParser  发现,最多的讲解是:opencv源码解析之(5):CommandLi ...

  5. 出现Fatal IO error 11 (资源暂时不可用) on X server :0.0.的可能原因及解决方案

    我在使用python的过程当中发现了这个有这样的错误,后来看了下面这篇文档才知道原因所在. 最近在编写一个局域网的聊天工具,在编写客户端时,我把界面部分和事件处理函数写好后,准备再开一个线程用于接收服 ...

  6. MySQL 5.1.73升级为MySQL 5.5.35详解

    一.前言 二.概述 三.安装MySQL 5.1.73 四.升级为MySQL 5.5.35 五.总结 注,测试环境 CentOS 6.4 x86_64,MySQL 版本(5.1.73.5.5.35)目前 ...

  7. 重力感应操控(unity iphone)

    方案一:speed 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public var simulateAcceleromet ...

  8. JS的Document属性和方法小结

    Document想必大家并不陌生吧,在使用js的过程中会经常遇到它,那么它有哪些属性.哪些方法,在本文将以示例为大家详细介绍下,希望对大家有所帮助 document.title //设置文档标题等价于 ...

  9. MATLAB中trapz和cumtrapz函数

    这两个函数都是MATLAB中的内置函数,是基于梯形法则的数值积分公式 例如我们有函数y=x^3-2x-3,为了计算在[0,1]上的积分,可以这么做: 其中x和y分别是自变量和对应的值,trapz其实就 ...

  10. 详解MySQL大表优化方案( 转)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...