php 实现双向链表 及 建立链表耗时

这个就是为了学习而使用,php有自身的链表实现,功能更全面,百度搜索 php spl

<?php
class node {
	public $val = null;
	public $pre = null;
	public $nex = null;
	//存放该node的位置
	public $index = null;
	public $num = 1;
	public function __construct($val){
		$this->val = $val;
		$this->pre = null;
		$this->nex = null;
	}
}

class links{
	//链表头
	static $head = null;
	//链表尾
	static $end = null;
	//链表内容
	static $nodes = [];
	
	/**
	 * 添加一个节点
	 * @param int $val 节点内容
	 */
	public static function add($val){
		//初始化一个节点
		$newNode = new node($val);
		$newNode->index = count(static::$nodes);
		static::$nodes[$newNode->index] = $newNode;
		//链表为空
		if( static::$head === null ){
			//头和尾都指向这个节点
			static::$head = $newNode->index;
			static::$end = $newNode->index;
		}else{
			static::searchSet($newNode, static::$nodes[static::$head]);
		}
	}

	/**
	 * 搜索链表插入
	 * @param node $newNode 新节点
	 * @param node $node 链表中的节点,和新节点做比较
	 */
	public static function searchSet($newNode, $node){
		//新插入的节点值小于目标节点值,则在目标节点前插入
		if($newNode->val < $node->val){
			if($node->pre === null){//如果目标节点前一个指向的是null,则说明需要在链表开头插入
				static::$head = $newNode->index;
			}else{//否则将前一个节点的后指针指向新节点,新节点的前指针指向前一个节点
				$pre = static::$nodes[$node->pre];
				$pre->nex = $newNode->index;
				$newNode->pre = $pre->index;
			}

			//新节点的后指针指向目标节点
			$newNode->nex = $node->index;
			//目标节点前指针指向新节点
			$node->pre = $newNode->index;

		}elseif($newNode->val == $node->val){
			++$node->num;
		}else{//新节点和下个节点比较
			if($node->nex === null){//如果下个节点为null,说明在链表尾部插入新节点
				$node->nex = $newNode->index;
				$newNode->pre = $node->index;
				static::$end = $newNode->index;
			}else{
				static::searchSet($newNode, static::$nodes[$node->nex]);
			}
		}
	}

    /**
     * 输出
     * @param bool $isasc 是否为正序输出
     */
	public static function printfs($isasc=true){
        static $index = null;

		if($index === null){
			if($isasc){
				$index = static::$head;
			}else{
				$index = static::$end;
			}
		}
		
		echo static::$nodes[$index]->val . ' ';

		if($isasc){
			if(static::$nodes[$index]->nex === null) return;
			$index = static::$nodes[$index]->nex;
		}else{
			if(static::$nodes[$index]->pre === null) return;
			$index = static::$nodes[$index]->pre;
		}

        static::printfs($isasc);
	}
}


function microtimeFloat() {
        list($usec, $sec) = explode(" ", microtime());
        return ((float) $usec + (float) $sec);
}


$time = microtimeFloat();
$ary = range(0, 1000);
shuffle($ary);

foreach($ary as $v){
	links::add($v);	
}
$time2 = microtimeFloat();
links::printfs();
$time3 = microtimeFloat();
echo "\n\n";
links::printfs(0);
$time4 = microtimeFloat();

echo "---------------\n";
echo ($time2 - $time) . "\n";
echo ($time3 - $time2) . "\n";
echo ($time4 - $time3) . "\n";

发表在 php小程序 | 标签为 , | 留下评论

在redis中优化频繁操作redis产生多次链接引发的网络延时

在程序中可能存在频繁操作redis,每次操作redis都需要产生网络链接,虽然每次操作的返回处理非常快(几十甚至几毫米),但是非常多的redis操作在超高的并发请求中,还是有必要优化的,那么php中如何避免产生多次操作,每次都链接一次redis呢?其实和上篇文章的方法是一样,仅仅是把 multi 的参数指定为Redis::PIPELINE,但是这样操作禁止了原子性操作,代码如下:

<?php
$redis = new \Redis(); 
$redis->connect('127.0.0.1', 6379);
$key = 'watchkey';

$redis->watch($key);
//仅仅这里的参数不同
$redis->multi(Redis::PIPELINE);

sleep(3);
$redis->incr($key);
$redis->get($key);

$r = $redis->exec();
var_dump($r);

function getRedis(){
   $redis = new \Redis(); 
   $redis->connect('127.0.0.1', 6379); 
   return $redis;
}

和上次一样,连着执行3次看看结果:
redis访问优化
发现 watchkey 被执行了3次,watch 并没有起到作用。
这样的操作仅仅确保进程之间不会产生数据冲突,才能使用这个方法。

发表在 redis | 标签为 , | 留下评论

redis 事务之watch

事务保证原子性操作,在redis 中实现事务和mysql中实现事务的方法有些不一样。
在 mysql中实现事务,一般是启动一个事务,然后执行 select … for update 对某个要操作行进行锁定,然后如果并发的进程处理到锁定的行时,判定改行是否被其他的进程正在读(被锁定),如果是,则等待锁释放,然后继续执行结下来的操作,这也就是所谓的悲观锁。
在redis中实现事务也是有多个方法的,比如用 watch 监控某个key是否被其他的进程修改,watch乐观锁的实现方案,也就是说,先假定所有的进程之间的数据操作之间没有任何交集,执行一系列的操作,在最后提交的时候,如果发现被监控的key已经被其他的进程修改了(前面的假定失败),则提交时则返回false。
php中用redis 的 watch 实现的代码如下:

<?php
//创建redis连接
$redis = new \Redis(); 
$redis->connect('127.0.0.1', 6379);

//被监控的键
$key = 'watchkey';
//监控 $key,一定要在事务前监控这个键,因为在执行 multi 命令后,所有的命令将会进入到一个redis队列,然后保证这些命令可以不被其他程序打扰的情况下依次执行,这也是redis实现原子操作的方法
$redis->watch($key);

//启用原子操作(事务)
$redis->multi();
//为了测试结果,休眠3秒
sleep(3);
//修改被监控的键值,incr 是redis 的计数器,原子性操作,每次执行这个命令,将给 $key 自增 1
$redis->incr($key);
//在事务内得所有操作都将返回 redis 连接对象,只有在执行 exec 返回结果中,所有的返回数据将放到数组中返回
$redis->get($key);

//执行事务,如果事务执行失败则返回false,正确则返回事务中的所有返回结果
$r = $redis->exec();
var_dump($r);

把上面的程序保存到t.php中,然后在命令行执行三次,等待执行结果:
redis事务之watch
我们发现,只有第一次执行是成功的,接下来执行的都是失败的。在redis-cli查看 watchkey 的结果,发现也仅仅第一个事务被正确执行了。

发表在 redis | 标签为 , , | 留下评论

队列调度程序

消息队列分配任务,通常未解决大并发问题,我们通常需要用消息队列异步来处理一些请求,把需要处理的数据先放到队列里,然后另有程序监听队列队列,异步处理这些数据。
如果队列过多的话,那么一个程序处理起来就需要消耗很多时间,本程序解决的问题是按照队列内数据量的大小,分配异步执行程序的数量,当队列里没值的时候,就没有任何进程,只有当队列内有值,才分配异步执行进程。
本程序是监控的队列是Redis,使用了一些php方法,可以按自己需要修改。
QueueScheduling

发表在 Shell | 标签为 , | 21条评论

shell 版守护进程

本文地址: http://ivhong.com/shell-版守护进程/
shell 实现守护进程,包含两个文件:
1 ctl.sh 控制器文件
2 run.sh 守护进程文件

用法:
1 按照注释修改 ctl.sh 要守护的进程命令
2 ctl.sh [start | stop | restart]

压缩包下载:
守护进程

发表在 Shell | 标签为 , | 3条评论

crontab 重定向错误日志 加上时间

在开发中,我们避免不了写crontab脚本来异步执行一些东西,一般设置crontab用下面的方法

*/1 * * * * commond > /tmp/t.log 2>&1

后面的 2>&1 代表把标准错误输出指向标准输出,意思是当commond异常退出时,把异常退出时的日志也写到 /tmp/t.log 下面,这时记录的日志是没有时间信息的,就是说日志里的数据没有办法知道是什么时候打印出来的。
下面通过一个shell来解决这件事,代码如下

#!/bin/bash

if [ $# -gt 1 ]; then
	istring=''
	#去掉回车
	date=`date | tr -s ["\n"]`
	for (( i = 2; i <= $#; i++ ))
	do
		string=$string" "${!i}
	done	
	c=$date": "$string
	echo "" >> $1
	echo $c >> $1
fi

把上面的代码放到根目录下,保存为 datecrontalog.sh

然后在crontab -e用下面的脚本代替

*/1 * * * *  commond 2>&1 | xargs $HOME/datecrontalog.sh /tmp/t.log
发表在 Shell | 标签为 , | 留下评论

修改NetBeans 新建文件默认文件类型为UNIX

1. 安装插件 change-line-endings-on-save

2. 工具-》选项-》编辑器-》Line Endings -> Enable adjusting the line endings 前打勾,然后下面的下拉框选择 Unix(LF)

发表在 NetBeans | 标签为 , , | 留下评论

php 按首字母大写把字符串分隔成数组

// 按首字母大写把字符串分隔成数组
// 例如:AaaBbbCcc 分割成 ['Aaa','Bbb','Ccc']
// 例如:AABbbCCggg 分割成 ['AAABbb', 'CCggg']
// 例如:aaaBBbbCD  分割成['aaa','BBbb', 'CD']
function FUSplitStr($str){
    return  preg_split('/(?<=[a-z0-9])(?=[A-Z])/x', $str);
}
发表在 php函数集 | 3条评论

php利用迭代器遍历目录

//通过path,查找path下的所有的文件,当$sub = true时,便利所有子目录下的所有文件
function findFiles($path, $sub=false){
    $list = [];
    if($sub){
        $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($path),
                RecursiveIteratorIterator::SELF_FIRST
        );
    }else{
        $iterator = new DirectoryIterator($path);
    }

    foreach($iterator as $file){
        $fileName = $file->getFilename();
        $path = $file->getPath();
        $list[] = ['path'=>$path, 'filename'=>$fileName];
    }
    return $list;
}
发表在 php函数集 | 留下评论

yii2 随笔(七)依赖注入——(4)服务定位器

服务定位器定位器是依赖注入的一种解决方式,它包含依赖注入,在解决了依赖注入后,如果服务使用者和服务提供者不是用一个人,那么使用者要了解提供服务的必须参数,这样才能保证依赖的正确性,这就耦合了使用者和提供者,服务定位器就是解耦这部分的,服务提供者在 ServiceLocator 中注册服务(同时注册了依赖),仅仅告诉服务使用者那些服务的名称或者别名,那么对于服务提供者和使用者都是好的,使用者只需要知道提供者提供的什么服务,而不必知道依赖什么,服务提供者也不必为使用者“胡乱使用”服务而导致的bug所困扰。
那么yii2是怎么使用ServiceLocator呢?其实很简单如下

//魔术方法,
public function __get($name){
    //得到某个注册的方法
    if ($this->has($name)) {
        return $this->get($name);
    } else {
        return parent::__get($name);
    }
}
//魔术方法查看某个服务是否存在,源码略
public function __isset($name){}
//__isset()中调用,查看某个服务是否存在,源码略
public function has($id, $checkInstance = false){}
//得到某个服务
public function get($id, $throwException = true)
{
    if (isset($this->_components[$id])) {//如果是已经处理的服务,就直接返回
        return $this->_components[$id];
    }

    if (isset($this->_definitions[$id])) {//如定义了该服务
        $definition = $this->_definitions[$id];//得到服务的定义
        //如果服务是一个闭包,则把闭包注册到已经实例化的服务中,并且返回闭包
        if (is_object($definition) && !$definition instanceof Closure) {
            return $this->_components[$id] = $definition;
        } else {//其他的情况下通过依赖注入生成对象,并且注册为已处理,返回对象
            return $this->_components[$id] = Yii::createObject($definition);
        }
    } elseif ($throwException) {//如果抛出异常,则抛出异常
        throw new InvalidConfigException("Unknown component ID: $id");
    } else {//其他返回null
        return null;
    }
}
//注册一个服务
public function set($id, $definition)
{
    if ($definition === null) {//如果该服务的定义为null,则删除已经实例化的服务,返回空,用于注销已经实例化的并且保存过的服务的定义
        unset($this->_components[$id], $this->_definitions[$id]);
        return;
    }
    //清空已经实例化的服务
    unset($this->_components[$id]);
    //如果该服务的定义为一个对象,并且是一个可调用的结构
    if (is_object($definition) || is_callable($definition, true)) {
        // an object, a class name, or a PHP callable
        $this->_definitions[$id] = $definition;
    } elseif (is_array($definition)) {//如果该服务是一个配置数组
        // a configuration array
        if (isset($definition['class'])) {//如果有class键值,则直接注册为一个服务的定义
            $this->_definitions[$id] = $definition;
        } else {//是配置数组,但是没有指定class,则抛出异常
            throw new InvalidConfigException("The configuration for the \"$id\" component must contain a \"class\" element.");
        }
    } else {//什么都不是,抛出异常,非法注册服务
        throw new InvalidConfigException("Unexpected configuration type for the \"$id\" component: " . gettype($definition));
    }
}
//清空已经实例化过的服务和定义,代码略
public function clear($id){}
//得到已经实例化后的服务,或者得到可用的服务配置
public function getComponents($returnDefinitions = true){}
//注册所有的服务,这里的$components,就是你在config里写的 $config['components']值
public function setComponents($components){}

那么ServiceLocator是从什么时候介入的呢?我们继续开我们的index.php,注意下面那句话

(new yii\web\Application($config))->run();

我们查看Application

class Application extends \yii\base\Application
//继续追踪  \yii\base\Application
abstract class Application extends Module
//继续追踪  Module
class Module extends ServiceLocator

哈,终于找到丫了!!!我们的application 其实就一个服务定位器,我们在配置文件里配置的components,都是application的这个服务定位器注册的服务。这下知道为什么叫做 setComponents这个函数了吧,不明白继续往下看

yii 用 set[typename] 的函数来确保属性的可写性,在基类 yii\base\Object 的构造函数里使用了 Yii::configure($this, $config);这个会调用 setComponents 函数注册服务。

好啦,前后都联系上了,yii2使用的依赖注入和服务定位器,就说到这里。

发表在 yii2 | 标签为 , , , | 留下评论