协程及swoole协程及协程通信waitgroup

一、io阻塞问题
由于线程在操作io时需要从硬盘中读写文件时会阻塞住也就是停止运行等待io操作完成后才会继续往下执行程序。好了问题来了,程序线程就在那干等着io操作完成。会导致程序效率非常低。为什么会阻塞呢?硬盘操作的速度要比cpu慢很多。

二、协程解决io阻塞问题
那能不能在做io操作时,不要阻塞住,而是继续往下执行。等io操作完成后,再执行io操作完成后的代码。

这种遇到耗时操作挂起(yield)耗时操作,线程继续往下执行程序,耗时操作完成后恢复(resume),恢复就是执行耗时操作完成后回调程序。就种线程程序执行方式就是是协程。也解决了io阻塞等待问题。

但是也不是说协程越多性能越好,单个硬盘的读写速度还是有限的,你叫单个硬盘做一堆事,硬盘也处理不过来,所有使用协程的最好方式还是网络调用另一台机器来做io操作。

一般是用在水平分库的查询上。使用协程去远程连接水平分库后的数据库服务器进行查询。

什么是水平分库呢?
在这里插入图片描述

1、概念:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。

2、结果:

每个库的结构都一样

每个库中的数据不一样,没有交集

所有库的数据并集是全量数据

3、场景:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库的情况下。

4、分析:库多了,io和cpu的压力自然可以成倍缓解

三、回调与协程的区别
回调:

通常回调是以向一个方法传递另一个函数作为参数, 在该方法中, 于适当的时候调用传递的函数参数的形式呈现的, 回调的本质还是函数调用, 他与普通的函数调用的不同点在于, 普通的函数调用在编程时已经确定要调用哪个函数, 而回调机制是在运行时确定需要调用的函数的, 根据传递的作为参数的函数不同, 其行为也会不同

协程:

协程是指在一个函数运行到出现某种情况时暂停执行转而执行另一个函数, 这听起来似乎跟函数调用相同, 但协程并不是函数调用, 他的重点是执行过程中的暂停执行, 这与多线程执行时的线程切换相似, 但协程是单线执行的, 他没有多线程切换时的开销, 这也就使得协程在运行效率更高的同时没有了多线程中的共享资源问题, 不再需要锁来保证安全, 但是相对的协程也没有了多线程对多处理器资源的利用率优势, 不过这个缺点可以利用多进程方式来弥补

四、安装swoole实现 协程
安装
git clone 下载源码地址
根据文档,执行编译命令,修改php.ini

协程实验

<?php 
	Co\run(function () {
		go(function(){
			co::sleep(1);//模拟耗时操作 wait 1s 直实场景一般是mysql 或者 redis
		  	echo 1;//耗时1秒后执行
		});

		go(function(){//执行
		    echo 2;
		});
		go(function(){//执行
		    echo 3;
		});
		echo "hello\n";//程序开始
	});
?>

依次输输出2 3 hello 1 耗时操作的协程最后执行
在这里插入图片描述
我们熟悉的文件读写、网络通讯请求(MySQL、Redis、Http等)都是属于 I/O 密集型场景。

假设一次 SQL 查询为 100ms,在传统同步模式下,当前线程在这 100ms 的时间里,是不能做其它操作的。如果要执行十次这个 SQL,可能需要耗费 1s 以上。

而如果用协程,虽然不同协程之间也是按顺序执行,但是在前一个等待 100ms 期间,底层会调度 CPU,去执行其它协程的操作。也就是说,可能第一个查询还没返回结果,其它几个查询就已经发送给了 MySQL 并正在执行中了。如果开启十个协程,分别执行这个 SQL,可能只需要耗费 100+ms 即可完成。

那我们怎合并所有协程返回的结果呢?
可以使用swoole的watigroup

<?php
Co\run(function () {
    $wg = new \Swoole\Coroutine\WaitGroup();

    $result = [];

    $wg->add();//协程数量加1 

    $time = microtime(true);
    //启动第一个协程
    go(function () use ($wg, &$result) {//use 关键字 函数闭包 调用外部变量
        co::sleep(1);
        $result['data0'] = "lala";
        echo "协程1 任务完成\n";
        $wg->done();//本协程任务完成
    });	

    $wg->add();//协程数量加1
    //启动第二个协程
    go(function () use ($wg, &$result) {
        co::sleep(2);
        $result['data1'] = "lala";
        echo "协程2 任务完成\n";
        $wg->done();//本协程任务完成
    });

    //挂起父协程,等待所有子协程任务完成后恢复
    $wg->wait();

    echo "所有协程任务完成,总耗时".(microtime(true)-$time)."s\n";
    //这里 $result 包含了 2 个任务执行结果
    var_dump($result);
});


在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章