首页
下载
文档
社区
视频
捐赠
源代码
赞助商
AOT 编译器
AI 助理
商业产品
PHP AOT 原生编译器
Swoole-Compiler 代码加密器
CRMEB 新零售社交电商系统
登录
注册
全部
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
发表新帖
在使用 协程+通道+waitgroup 遇到 channel::pop()报错问题
### 问题描述 PHP Fatal error: Swoole\Coroutine\Channel::pop(): you must call Channel constructor first in @swoole-src/library/core/Coroutine/WaitGroup.php on line 61 ### Swoole版本,PHP版本,以及操作系统版本信息 swoole : 4.5.2 / 4.4.19 php:7.2 os:ubuntu20.04 ### 相关代码 ```php if (!extension_loaded('swoole')) { die('Abort ' . basename(__FILE__) . ' : Missing swoole extensions'); } echo "swoole-并发执行采集-开始\n"; $requestUrl = "https://cn.bing.com/search?q=urldecode"; $urlArr = parse_url($requestUrl); $host = $urlArr['host']; $port = isset($urlArr['port']) ? $urlArr['port'] : 80; $uri = sprintf("%s", $urlArr['path'], $urlArr['query']); Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); Co\run(function () use ($host, $port, $uri) { $inWg = new Swoole\Coroutine\WaitGroup(); $outWg = new Swoole\Coroutine\WaitGroup(); $inChan = new Swoole\Coroutine\Channel(10); $outChan = new Swoole\Coroutine\Channel(10); $stChan = new Swoole\Coroutine\Channel(1); for ($i = 1; $i <= 10 $i++) { $inWg->add(); go(function () use ($host, $port, $uri, $i, $inChan, $outChan, $inWg, $stChan) { while(1){ if($stChan->pop() === "done"){ echo "st协程:close->out协程{$i}:close\n"; break; } $pg = $inChan->pop(); if ($pg === false) { echo "in协程:close->out协程{$i}:close\n"; break; } $cli = new Swoole\Coroutine\Http\Client($host, $port); $cli->set( array( 'timeout' => 3 ) ); $cli->setHeaders( array( 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36', ) ); $uri = sprintf("%s&page=%d", $uri, $pg); $cli->get($uri); $html = $cli->body; $cli->close(); if(strlen($html) > 0){ $outChan->push(array($pg, $html)) ; }else{ $inChan->push($pg); } //出让协程执行 co::sleep(0.05); } $inWg->done(); }); } for ($j = 1; $j <= 10; $j++) { $outWg->add(); go(function () use ($j, $outChan, $outWg, $stChan) { while(1){ if($stChan->pop() === "done"){ echo "status协程:close->out协程{$j}:close\n"; break; } $data = $outChan->pop(); if ($data === false) { echo "out协程:close->out协程{$j}:close\n"; break; } $filePath = sprintf("./file/%d.log", $data[0]); $ret = file_put_contents($filePath, $data[1]); $len = strlen($data[1]); echo "out协程-{$j}->写入文件{$filePath}->size:{$len}->ret:{$ret}\n"; co::sleep(0.05); } $outWg->done(); }); } //提交初始数据 for ($i = 1; $i <= 100; $i++) { $inChan->push($i); } //等待协程执行完毕 $inWg->wait(); $outWg->wait(); //关闭监控通道 $stChan->push("done"); $stChan->close(); //关闭中间数据通道 $outChan->close(); $inChan->close(); }); echo "swoole-并发执行采集-结束\n"; ``` ### 你期待的结果是什么?实际看到的错误信息又是什么? 是否我上述的用法不对呢?,希望能正常自动完成10页数据的输出内容到文件中, 错误在上面。
发布于6年前 · 7 次浏览 · 来自
提问
紫檀v2
### 问题描述 PHP Fatal error: Swoole\Coroutine\Channel::pop(): you must call Channel constructor first in @swoole-src/library/core/Coroutine/WaitGroup.php on line 61 ### Swoole版本,PHP版本,以及操作系统版本信息 swoole : 4.5.2 / 4.4.19 php:7.2 os:ubuntu20.04 ### 相关代码 ```php if (!extension_loaded('swoole')) { die('Abort ' . basename(__FILE__) . ' : Missing swoole extensions'); } echo "swoole-并发执行采集-开始\n"; $requestUrl = "https://cn.bing.com/search?q=urldecode"; $urlArr = parse_url($requestUrl); $host = $urlArr['host']; $port = isset($urlArr['port']) ? $urlArr['port'] : 80; $uri = sprintf("%s", $urlArr['path'], $urlArr['query']); Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); Co\run(function () use ($host, $port, $uri) { $inWg = new Swoole\Coroutine\WaitGroup(); $outWg = new Swoole\Coroutine\WaitGroup(); $inChan = new Swoole\Coroutine\Channel(10); $outChan = new Swoole\Coroutine\Channel(10); $stChan = new Swoole\Coroutine\Channel(1); for ($i = 1; $i <= 10 $i++) { $inWg->add(); go(function () use ($host, $port, $uri, $i, $inChan, $outChan, $inWg, $stChan) { while(1){ if($stChan->pop() === "done"){ echo "st协程:close->out协程{$i}:close\n"; break; } $pg = $inChan->pop(); if ($pg === false) { echo "in协程:close->out协程{$i}:close\n"; break; } $cli = new Swoole\Coroutine\Http\Client($host, $port); $cli->set( array( 'timeout' => 3 ) ); $cli->setHeaders( array( 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36', ) ); $uri = sprintf("%s&page=%d", $uri, $pg); $cli->get($uri); $html = $cli->body; $cli->close(); if(strlen($html) > 0){ $outChan->push(array($pg, $html)) ; }else{ $inChan->push($pg); } //出让协程执行 co::sleep(0.05); } $inWg->done(); }); } for ($j = 1; $j <= 10; $j++) { $outWg->add(); go(function () use ($j, $outChan, $outWg, $stChan) { while(1){ if($stChan->pop() === "done"){ echo "status协程:close->out协程{$j}:close\n"; break; } $data = $outChan->pop(); if ($data === false) { echo "out协程:close->out协程{$j}:close\n"; break; } $filePath = sprintf("./file/%d.log", $data[0]); $ret = file_put_contents($filePath, $data[1]); $len = strlen($data[1]); echo "out协程-{$j}->写入文件{$filePath}->size:{$len}->ret:{$ret}\n"; co::sleep(0.05); } $outWg->done(); }); } //提交初始数据 for ($i = 1; $i <= 100; $i++) { $inChan->push($i); } //等待协程执行完毕 $inWg->wait(); $outWg->wait(); //关闭监控通道 $stChan->push("done"); $stChan->close(); //关闭中间数据通道 $outChan->close(); $inChan->close(); }); echo "swoole-并发执行采集-结束\n"; ``` ### 你期待的结果是什么?实际看到的错误信息又是什么? 是否我上述的用法不对呢?,希望能正常自动完成10页数据的输出内容到文件中, 错误在上面。
赞
0
收藏
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
登录
后参与评论
评论
2020-07-29
鲁飞
你自己代码有问题,把wait放到后面。 ``` //关闭监控通道 $stChan->push("done"); $stChan->close(); //关闭中间数据通道 $outChan->close(); $inChan->close(); //等待协程执行完毕 $inWg->wait(); $outWg->wait(); ```
赞
0
回复
2020-07-30
紫檀v2
回复
鲁飞
通道关闭,不应该要等到所有协程执行完毕后吗,wait()不就是要等到协程执行结束的吗,为啥wait()要放到最后呢?请大神赐教。
赞
0
回复
2020-07-30
紫檀v2
回复
鲁飞
按照你把wait()放到最后,协程中的采集内容协程,写入文件文件协程,没有一个执行的。
赞
0
回复
2020-07-30
鲁飞
回复
紫檀v2
因为都是false了,你都break掉了。
赞
0
回复
2020-07-30
鲁飞
实际上一个waitgroup和Channel就能实现你的需求。写的太复杂了 ```php Co\run(function () { $wg = new \Swoole\Coroutine\WaitGroup(); $outChan = new Swoole\Coroutine\Channel(1); $requestUrl = "https://cn.bing.com/search?q=urldecode"; $urlArr = parse_url($requestUrl); $host = $urlArr['host']; $port = isset($urlArr['port']) ? $urlArr['port'] : 80; $uri = sprintf("%s?%s", $urlArr['path'], $urlArr['query']); for ($i = 1; $i <= 10;$i++) { $wg->add(); go(function () use ($outChan, $i, $host, $port, $uri) { $cli = new Swoole\Coroutine\Http\Client($host, $port); $cli->set( array( 'timeout' => 3 ) ); $cli->setHeaders( array( 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36', ) ); $uri = sprintf("%s&page=%d", $uri, $i); $cli->get($uri); $html = $cli->statusCode; $cli->close(); if(strlen($html) > 0){ $outChan->push(array($i, $html)) ; } //出让协程执行 co::sleep(0.05); }); $wg->done(); } $wg->add(); go(function () use ($wg, $outChan) { while (true) { Co::sleep(1); var_dump($outChan->pop()); } $wg->done(); }); $wg->wait(); }); ```
赞
0
回复
2020-07-30
紫檀v2
回复
鲁飞
在用双通道是因为在采集的时候多协程会出现某些页面获取内容失败,需要返回采集通道重新采集直到完全采集完毕。输出通道多协程,是为了方便快速输出内容。看你的修改后,我暂且先退化输出使用单协程处理好了。如果有后续好的方法我会跟帖完善的,在这里感谢鲁飞大神的回复,
赞
0
回复