V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yuandj
V2EX  ›  PHP

关于 swoole 的协程和 channel 使用遇到的问题

  •  
  •   yuandj · 2021-01-06 12:28:35 +08:00 · 1231 次点击
    这是一个创建于 543 天前的主题,其中的信息可能已经有所发展或是发生改变。
    $qpsChannel = new Channel();
    
    go(function () use ($qpsChannel, $configs) {
    	$waitGroup = new WaitGroup();
    
    	foreach ($configs as $key => $config) {
    		$waitGroup->add();
    		go(function () use ($qpsChannel, $waitGroup, $key, $config) {
    			if ($this->skipConfigByStatus($config) === false
    			&& $this->skipConfigByQps($config) === false) {
    				$qpsChannel->push('111111');
    			} else {
    				$qpsChannel->push('222222');
    			}
                		$waitGroup->done();
    		});
    	}
    
    	$waitGroup->wait();
    	$qpsChannel->close();
    });
    
    while (true) {
    	$qpsStatusArr = $qpsChannel->pop();
    	var_dump($qpsStatusArr);
    	if ($qpsStatusArr === false) break;
    }
    

    上面代码逻辑是打开一个子协程去循环判断一些逻辑,并把结果写入到 channel 中,处理完之后,从子协程把 channle 关闭(关闭之后再操作 channel 就会返回 false)

    正常的逻辑应该是打开子协程去处理逻辑,然后进入到 while 去读取 channle,当子协程有 channel push 时,while 中的逻辑会把结果打印出来

    现在遇到的问题是只打印出一个 false,就结束了。难道是子协程处理时间太快了,还没走到 while,channle 就被 close 了?

    问题 1:求大神解答上面代码问题。

    问题 2:大家都是怎么在代码中用子协程去节省时间呢?写法是什么样的呢?

    第 1 条附言  ·  2021-01-06 15:04:26 +08:00

    可能区别在于golang中的chan和swoole中的channel

    在golang中chan要在生产者方关闭chan,当chan被close之后,for range还是可以读取到chan中未消费的数据。

    在swoole中,当channel被close之后,读取不到未消费的数据。

    swoole文档中介绍:

    Close()方法:唤醒所有生产者协程,push 方法返回 false;唤醒所有消费者协程,pop 方法返回 false
    

    下面为修改过后的版本:

    $qpsChannel = new Channel();
    
    go(function () use ($qpsChannel, $configs) {
         $waitGroup = new WaitGroup();
    
         foreach ($configs as $key => $config) {
             $waitGroup->add();
              go(function () use ($qpsChannel, $waitGroup, $key, $config) {
                   if ($this->skipConfigByStatus($config) === false
                       && $this->skipConfigByQps($config) === false) {
                            $qpsChannel->push([$key => false]);
                   } else {
                            $qpsChannel->push([$key => true]);
                   }
                 $waitGroup->done();
             });
           }
    
         $waitGroup->wait();
         $qpsChannel->push(self::CHANNEL_CLOSE);
    });
    
    while (true) {
           $qpsStatus = $qpsChannel->pop();
    
           if ($qpsStatus === self::CHANNEL_CLOSE) {
                 $qpsChannel->close();
                 break;
           }
    
           if (is_array($qpsStatus)) {
                 $configKey = array_keys($qpsStatus)[0];
                 $configSkip = array_values($qpsStatus)[0];
                 if ($configSkip === true) unset($configs[$configKey]);
           }
    }
    

    在生产者方push一个结束的标识,从而让消费者方退出阻塞和关闭channel

    3 条回复    2021-01-06 13:33:00 +08:00
    mxtob
        1
    mxtob  
       2021-01-06 13:05:39 +08:00 via iPhone
    wg
    chan
    for 10
    go
    i <-chan pop
    push i
    wg done


    for data i
    chan push i
    wg add

    wg done
    chan close

    这样行吗
    mxtob
        2
    mxtob  
       2021-01-06 13:08:39 +08:00 via iPhone
    倒数第二句是 wg wait
    yuandj
        3
    yuandj  
    OP
       2021-01-06 13:33:00 +08:00
    @mxtob 把读写颠倒一下,是个不错的想法。
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4552 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 02:22 · PVG 10:22 · LAX 19:22 · JFK 22:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.