workman源代码阅读 - 使用信号处理器实现定时器

<?php

/**
 * SIGALRM信号处理器注册成功后,在什么情况下进程会收到该信号呢?
 *
 * 在Linux系统下,每个进程都有惟一的一个定时器,该定时器提供了以秒为单位的定时功能。在定时器设置的超时时间到达后,调用alarm的进程将收到SIGALRM信号。
 */

/**
 * 启动信号处理器
 */
\MySignalClazz::init();

/**
 * 信号处理器
 * @author Administrator
 *
 */
class MySignalClazz {

	/**
	 * Tasks that based on ALARM signal.
	 * [
	 *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
	 *   run_time => [[$func, $args, $persistent, time_interval],[$func, $args, $persistent, time_interval],..]],
	 *   ..
	 * ]
	 *
	 * @var array
	 */
	protected static $_tasks = array();

	/**
	 * 注册信号处理器
	 */
	public static function init()
	{
		pcntl_signal(SIGALRM, array(‘\MySignalClazz‘, ‘signalHandle‘), false);
	}

	/**
	 * 信号处理器
     * ALARM signal handler.
     * @return void
     */
	public static function signalHandle(){
		pcntl_alarm(1); // 创建一个计时器,在1秒后向进程发送一个SIGALRM信号
		self::tick();
	}

	/**
	 * Add a timer.
	 *
	 * @param int      $time_interval
	 * @param callback $func
	 * @param mixed    $args
	 * @param bool     $persistent
	 * @return bool
	 */
	public static function add($time_interval, $func, $args = array(), $persistent = true)
	{
		if ($time_interval <= 0) {
			echo new Exception("bad time_interval");
			return false;
		}

		if (!is_callable($func)) {
			echo new Exception("not callable");
			return false;
		}

		if (empty(self::$_tasks)) {
			pcntl_alarm(1); // 重新启动定时器,实现1秒定时
		}

		$time_now = time();
		$run_time = $time_now + $time_interval; // 计算执行时间
		if (!isset(self::$_tasks[$run_time])) {
			self::$_tasks[$run_time] = array();
		}
		self::$_tasks[$run_time][] = array($func, (array)$args, $persistent, $time_interval);
		return true;
	}

	/**
	 * 单次执行函数
	 */
	public static function tick()
    {
        if (empty(self::$_tasks)) {
            pcntl_alarm(0);  // 不在创建新的信号
            return;
        }

        $time_now = time();
        foreach (self::$_tasks as $run_time => $task_data) {
            if ($time_now >= $run_time) { // 到达执行时间
                foreach ($task_data as $index => $one_task) {
                    $task_func     = $one_task[0];
                    $task_args     = $one_task[1];
                    $persistent    = $one_task[2];
                    $time_interval = $one_task[3];
                    try {
                        call_user_func_array($task_func, $task_args); // 执行注册的函数
                    } catch (\Exception $e) {
                        echo $e;
                    }
                    if ($persistent) {
                        self::add($time_interval, $task_func, $task_args); // 重新计算下次执行时间,添加进去
                    }
                }
                unset(self::$_tasks[$run_time]); // 执行完就删除掉
            }
        }
    }
}
时间: 2024-12-16 12:12:18

workman源代码阅读 - 使用信号处理器实现定时器的相关文章

非常好!!!Linux源代码阅读——中断【转】

Linux源代码阅读——中断 转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/2_int.html 目录 为什么要有中断 中断的作用 中断的处理原则 Linux 中断机制 中断控制器 中断描述符 中断数据结构 中断的初始化 内核接口 中断处理过程 CPU 的中断处理流程 保存中断信息 处理中断 从中断中返回 编写中断处理程序 软中断.tasklet与工作队列 上半部与下半部 软中断 tasklet 工作队列 1 为什么要有中断 1.1 中

OpenJDK 源代码阅读之 Collections

概要 类继承关系 java.lang.Object java.util.Collections 定义 public class Collections extends Object 实现 sort public static <T extends Comparable<? super T>> void sort(List<T> list) { Object[] a = list.toArray(); Arrays.sort(a); ListIterator<T&g

Notepad++源代码阅读——窗口元素组织与布局

1.1 前言 这两天在看notepad++ 1.0版本的源代码.看了许久终于把程序的窗口之间的关系搞清楚了现在把其组织的要点写于此,希望对大家有所帮助. 1.2 窗口元素之间的关系 Notepad++主要有以下窗口元素(见下图). 其中Notepad_plus 是程序的主要窗口,其他:工具栏.状态栏.主次编辑窗口.主次选项卡窗口以及对话框窗口均为主窗口的子窗口.     _mainDocTab 和 _subDocTab 为 类:DocTabView 其成员_pView 分别指向 _mainEdi

Linux-0.11源代码阅读一 加载操作系统

x86系列CPU可以在16位实模式和32位保护模式下运行,实模式的特点是地址总线只有20位,也就是只有1MB的寻址空间,为了兼容老的CPU,Intel x86系列CPU包括最新的CPU在上电时都运行在16位的实模式下,同时在硬件上强行将CS置成0xF000,IP置成0xFFF0,那么CS:IP就指向0xFFFF0这个地址,也就是上电瞬间代码从该处开始执行,而BIOS恰恰就存储在这个地方,可以想象一下,如果连BIOS都没有将会是一个什么结果. BIOS程序被存储在计算机主板上的一块ROM芯片里,首

linux0.11 源代码阅读记录

*/--> pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;}

淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成物理查询计划

SQL编译解析三部曲分为:构建语法树,制定逻辑计划,生成物理运行计划. 前两个步骤请參见我的博客<<淘宝数据库OceanBase SQL编译器部分 源代码阅读--解析SQL语法树>>和<<淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成逻辑计划>>.这篇博客主要研究第三步,生成物理查询计划. 一. 什么是物理查询计划 与之前的阅读方法一致,这篇博客的两个主要问题是what 和how.那么什么是物理查询计划?物理查询计划可以直接运行并返回数据

TLD matlab源代码阅读(2)

今天继续,下面是开始要生成正负例来训练分类器了,首先: // TRAIN DETECTOR ========================================================== // Initialize structures tld.imgsize = size(tld.source.im0.input); //为fern准备的训练集 tld.X = cell(1,length(tld.source.idx)); //training data for fern

Linux-0.11源代码阅读二 实模式到保护模式

bootsect部分已经执行完成,程序也跳转到setup部分: start: ! ok, the read went well so we get current cursor position and save it for ! posterity. mov ax,#INITSEG ! this is done in bootsect already, but... mov ds,ax mov ah,#0x03 ! read cursor pos xor bh,bh int 0x10 ! sa

【转】Tomcat总体结构(Tomcat源代码阅读系列之二)

本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Eclipse中运行Tomcat源代码,本文介绍一下Tomcat的总体结构. 本文没有特别指明的地方,源代码都是针对tomcat7.0.42来说. Tomcat的总体结构 Tomcat即是一个Http服务器也是一个Servlet容器,它的总体结构我们可以用下图来描述: 通过上图我们可以看出Tomcat中