Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

请问在Swow 的协程中是否可以使用 sleep方法 #231

Open
dalux opened this issue Nov 22, 2023 · 8 comments
Open

请问在Swow 的协程中是否可以使用 sleep方法 #231

dalux opened this issue Nov 22, 2023 · 8 comments
Labels
discussion Discuss things in this issue

Comments

@dalux
Copy link

dalux commented Nov 22, 2023

最近在尝试用 swow 写一点试验性的东西,但发现在协程中使用了 sleep() 后,程序逻辑就无法正常运行了,代码如下:

use Swow\Coroutine;
use Swow\Sync\WaitGroup;

$wg = new WaitGroup();
$wg->add(2);

for ($i = 0; $i < 2; $i++) {
    Coroutine::run(function() use ($wg) {
        var_dump('Goroutine #'. Coroutine::getCurrent()->getId());
        $oracle = new Oracle("127.0.0.1", 1521, "uname", "passwd", "dbname");
        $oracle->beginTransaction();
        var_dump($oracle->fetchScalar("select * from table_name where id='A00011752209' for update"));
        sleep(5);    //此处 sleep 5 秒,模拟数据库执行时间 
        $oracle->commit();
        var_dump('Goroutine #'. Coroutine::getCurrent()->getId(). " done");
        $wg->done();
    });
}

$wg->wait();
var_dump('done');

如果不加 sleep,上面的代码是正常的,输出如下:

string(12) "Goroutine #2"
string(3) "269"
string(17) "Goroutine #2 done"
string(12) "Goroutine #3"
string(3) "269"
string(17) "Goroutine #3 done"
string(4) "done"

但我加上 sleep(5)后,就会卡在第一个协程里的 sleep 处,一直无法继续后续响应:

string(12) "Goroutine #2"
string(3) "269"
string(12) "Goroutine #3"

是否协程中无法使用系统原生 sleep?我知道 swoole 中的协程是不能这样用的,Swow 是否也一样?可有什么替代的方式?

@dalux dalux added the discussion Discuss things in this issue label Nov 22, 2023
@twose
Copy link
Member

twose commented Nov 22, 2023

感觉是因为没有支持 Oracle 的协程化,sleep 是支持的,你把数据库逻辑部分去掉应该就可以验证这个事情了。

@dalux
Copy link
Author

dalux commented Nov 23, 2023

但我尝试改了一下上面的代码,如下:

use Swow\Coroutine;
use Swow\Sync\WaitGroup;

$wg = new WaitGroup();
$wg->add(2);

for ($i = 0; $i < 2; $i++) {
    Coroutine::run(function() use ($wg) {
        var_dump('Coroutine #'. Coroutine::getCurrent()->getId());
        $oracle = new Oracle("127.0.0.1", 1521, "uname", "passwd", "dbname");
        $oracle->beginTransaction();
        var_dump($oracle->fetchScalar("select * from table_name where id='A00011752209'"));
        $rnd = rand(1,10);
        var_dump($rnd);
        sleep($rnd);    //此处 sleep 若干 秒,模拟数据库执行时间  
        $oracle->commit();
        var_dump('Coroutine #'. Coroutine::getCurrent()->getId(). " done");
        $wg->done();
    });
}

$wg->wait();
var_dump('done');

只是去掉了 for update 行锁,同时对不同的 coroutine 业务 sleep 了不同的时间,输出如下:

string(12) "Coroutine #2"
string(3) "269"
int(8)
string(12) "Coroutine #3"
string(3) "269"
int(4)
string(17) "Coroutine #3 done"
string(17) "Coroutine #2 done"
string(4) "done"

从结果上看,协程内 sleep 使用确实是没问题的。如上,Coroutine #3 后启动,但因 sleep 时间较短,反而先结束,应该从侧面也证明了 oracle 是有协程化的,那问题出在 for update 上吗?只要有 for update语句,再加上 sleep,业务就会卡住,不知为何?

@xywf221
Copy link

xywf221 commented Nov 23, 2023

如果oracle是有协程化你连接oracle就会挂起,执行其他协程去了。你上面这段代码很好解释第一个协程sleep触发了协程挂起向下执行代码开启了第二个协程,这个时候第二个协程sleep也挂起了这时候代码应该跑到$wg->wait(),再次触发挂起这时候所有协程处于挂起,由于你第二个协程sleep时间短所以第二个协程就先恢复了

@xywf221
Copy link

xywf221 commented Nov 23, 2023

关于你第一个代码提出来的问题。感觉很复杂我个人感觉是事务问题。不太懂oracle可能是事务没有commit导致for update或者beginTransaction的卡主了

@dalux
Copy link
Author

dalux commented Nov 23, 2023

如果oracle是有协程化你连接oracle就会挂起,执行其他协程去了。你上面这段代码很好解释第一个协程sleep触发了协程挂起向下执行代码开启了第二个协程,这个时候第二个协程sleep也挂起了这时候代码应该跑到$wg->wait(),再次触发挂起这时候所有协程处于挂起,由于你第二个协程sleep时间短所以第二个协程就先恢复了

3Q,明白了。

@zxj-code
Copy link

问个无关的问题哈。Swoole 是通过 hook 阻塞方法实现的协程调度,Swow 是怎么做到遇到 sleep 这样的阻塞方法发生协程调度的?

@dixyes
Copy link
Member

dixyes commented Dec 30, 2023

问个无关的问题哈。Swoole 是通过 hook 阻塞方法实现的协程调度,Swow 是怎么做到遇到 sleep 这样的阻塞方法发生协程调度的?

我不太清楚你说的hook是指什么 如果指的是php函数“zif_sleep()” 被换成了sw*的函数"zif_sw*_sleep()" 那sw*的做法是一致的

如果你问的是实现它们的东西:

php本身是调用sleep(unix)/Sleep函数(windows),操作系统会暂停当前线程,吧cpu用于其他线程
swow是通过注册libuv(libcat)的回调来实现:
libuv则是通过epoll、iouring、kqueue、iocp等异步io机制注册回调,然后libcat选择去执行另一个(可能是sleep完事的 可能是fd可读的)协程
swoole我不太清楚,估摸着也大差不差 只不过中间层是libswoole

@zxj-code
Copy link

zxj-code commented Jan 3, 2024

问个无关的问题哈。Swoole 是通过 hook 阻塞方法实现的协程调度,Swow 是怎么做到遇到 sleep 这样的阻塞方法发生协程调度的?

我不太清楚你说的hook是指什么 如果指的是php函数“zif_sleep()” 被换成了sw的函数"zif_sw_sleep()" 那sw*的做法是一致的

如果你问的是实现它们的东西:

php本身是调用sleep(unix)/Sleep函数(windows),操作系统会暂停当前线程,吧cpu用于其他线程 swow是通过注册libuv(libcat)的回调来实现: libuv则是通过epoll、iouring、kqueue、iocp等异步io机制注册回调,然后libcat选择去执行另一个(可能是sleep完事的 可能是fd可读的)协程 swoole我不太清楚,估摸着也大差不差 只不过中间层是libswoole

好的,谢谢。看来处理阻塞方法:sleep、pdo、fopen、fread 等等,swow 和 swoole 的思路是一致的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Discuss things in this issue
Projects
None yet
Development

No branches or pull requests

5 participants