gpt4 book ai didi

multithreading - 莫霍利奇 : Limiting number of Promises/IOLoop->subprocess

转载 作者:行者123 更新时间:2023-12-02 00:58:32 32 4
gpt4 key购买 nike

我正在使用 Mojolicious 非阻塞方法(Promises)从外部系统请求数据。 1)我想立即通知用户进程已经开始; 2) 我想扩展这个程序。

下面的代码适用于一小部分数字(几百个),如果数字更多,我会得到一个错误 [error] Can't create pipe: Too many open files at/path/lib/perl5/Mojo/IOLoop.pm 第 156 行。 问题 1)我如何限制我生成的 Promise 的数量(map 在我下面的代码中):

#!/usr/bin/env perl

use Mojolicious::Lite;
use Mojolicious::Plugin::TtRenderer;

sub isPrime
{
my ($n) = @_;
my $e = sqrt($n);
for (my $i=2; $i<$e; $i++) {
return 0 if $n%$i==0;
}
return 1;
}

sub makeApromise
{
my ($number) = @_;

my $promise = Mojo::Promise->new;
Mojo::IOLoop->subprocess(
sub { # first callback is executed in subprocess
my %response;
# Simulate a long computational process
$response{'number'} = $number;
$response{'isPrime'} = isPrime($number);
return \%response;
},
sub { # second callback resolves promise with subprocess result
my ($self, $err, @result) = @_;
return $promise->reject($err) if $err;
$promise->resolve(@result);
},
);
return $promise;
}

plugin 'tt_renderer'; # automatically render *.html.tt templates

any '/' => sub {
my ($self) = @_;
my $lines = $self->param( 'textarea' );

if ($lines) {
my @numbers;
foreach my $number (split(/\r?\n/, $lines)) {
push(@numbers, $number) if $number =~ /^\d+$/;
}
if (@numbers) {
####################################
### This is the problem below... ###
my @promises = map { makeApromise($_) } @numbers;
####################################
# MojoPromise Wait
Mojo::Promise->all(@promises)
->then(sub {
my @values = map { $_->[0] } @_;
foreach my $response (@values) {
#print STDERR $response->{'number'}, " => ", $response->{'isPrime'}, "\n";
# Prepare email...
}
# Send an email...
})
#->wait # Don't wait? I want to tell the user to wait for an email as quickly as possible...
if @promises;
}
$self->stash(done => "1",);
}
$self->render(template => 'index', format => 'html', handler => 'tt');
};

app->start;
__DATA__

@@ index.html.tt
<!DOCTYPE html>
<html lang="en">
<head>
<title>Make A Promise</title>
</head>
<body>
[% IF done %]
<h3>Thank you! You will receive an email shortly with the results.</h3>
[% ELSE %]
<h3>Enter numbers...</h3>
<form role="form" action="/" method="post">
<textarea name="textarea" rows="5" autofocus required></textarea>
<button type="submit">Submit</button>
</form>
[% END %]
</body>
</html>

我注释掉了wait;但是,代码似乎仍在阻塞。 问题 2) 如何立即通知用户进程已经开始? (即当我 stash done 变量时)

最佳答案

问题不在于 promise 的数量,而在于子流程的数量。限制这种情况的一种方法是简单地限制您在程序逻辑中一次创建的数量。不要在 map 中一次生成它们,而是设置一个限制并从@numbers 中检索那么多(可能使用 splice )并生成这些子进程;创建一个 ->all promise ,等待这些 promise 并附加一个 ->then 到该 promise 以检索您的下一个数字 block ,依此类推。

另一种选择是使用 Future::Utils fmap_concat 可以通过提供最大未完成 future 数量来处理限速代码。您的 promise 返回功能可以应用 Mojo::Promise::Role::Futurify链接以下 Future 以这种方式使用。

#!/usr/bin/env perl

use Mojolicious::Lite;
use Mojo::File 'path';
use Mojo::IOLoop;
use Mojo::Promise;
use Future::Utils 'fmap_concat';

get '/' => sub {
my $c = shift;
my $count = $c->param('count') // 0;
my @numbers = 1..$count;

if (@numbers) {
my $result_f = fmap_concat {
my $number = shift;
my $p = Mojo::Promise->new;
Mojo::IOLoop->subprocess(sub {
sleep 2;
return $number+1;
}, sub {
my ($subprocess, $err, @result) = @_;
return $p->reject($err) if $err;
$p->resolve(@result);
});
return $p->with_roles('Mojo::Promise::Role::Futurify')->futurify;
} foreach => \@numbers, concurrent => 20;

$result_f->on_done(sub {
my @values = @_;
foreach my $response (@values) {
$c->app->log->info($response);
}
})->on_fail(sub {
my $error = shift;
$c->app->log->fatal($error);
})->retain;

$c->stash(done => 1);
}
$c->render(text => "Processing $count numbers\n");
};

app->start;

至于 wait 方法,当事件循环已经在运行时它什么都不做,如果你在 Mojolicious 守护进程中启动应用程序(而不是 PSGI 或 CGI 服务器,它会在 webapp 响应处理程序中运行) ' 支持异步响应)。回调之外的 ->stash 和 ->render 调用将在设置子进程后立即运行。然后响应处理程序将完成,事件循环将再次获得控制权,一旦 promise 解决,它将触发适当的 ->then 回调。渲染不应该等待子进程设置之外的任何事情;既然你说可能有数百个,那可能就是你正在经历的放缓。确保您使用的是 Mojolicious 7.86 或更新版本,因为 Subprocess 已更改,以便在事件循环的下一次滴答(响应处理程序完成后)之前不会发生 fork 。

我还要指出,子流程并不是真正为此而设计的;它们旨在执行缓慢的代码,这些代码仍会在响应中向浏览器返回最终结果(并且 Mojolicious::Plugin::Subprocess 非常适合此用例)。我看到的一个问题是,如果您重新启动应用程序,任何仍在挂起的子进程都将被忽略。对于您想要开始并忘记的工作,您可以考虑像 Minion 这样的工作队列。它与 Mojolicious 应用程序完美集成,并通过单独的工作进程运行。

关于multithreading - 莫霍利奇 : Limiting number of Promises/IOLoop->subprocess,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52393335/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com