Non-blocking / Asynchronous Execution in Perl

2020-06-09 06:49发布

问题:

Is there a way to implement non-blocking / asynchronous execution (without fork()'ing) in Perl?

I used to be a Python developer for many years... Python has really great 'Twisted' framework that allows to do so (using DEFERREDs. When I ran search to see if there is anything in Perl to do the same, I came across POE framework - which seemed "close" enough to what I was searching for. But... after spending some time reading the documentation and "playing" with the code, I came against "the wall" - which is following limitation (from POE::Session documentation):

Callbacks are not preemptive. As long as one is running, no others will be dispatched. This is known as cooperative multitasking. Each session must cooperate by returning to the central dispatching kernel.

This limitation essentially defeats the purpose of asynchronous/parallel/non-blocking execution - by restricting to only one callback (block of code) executing at any given moment. No other callback can start running while another is already running!

So... is there any way in Perl to implement multi-tasking (parallel, non-blocking, asynchronous execution of code) without fork()'ing - similar to DEFERREDs in Python?

回答1:

Coro is a mix between POE and threads. From reading its CPAN documentation, I think that IO::Async does real asynchronous execution. threads can be used too - at least Padre IDE successfully uses them.



回答2:

I'm not very familiar with Twisted or POE, but basic parallel execution is pretty simple with threads. Interpreters are generally not compiled with threading support, so you would need to check for that. The forks package is a drop-in replacement for threading (implements the full API) but using processes seamlessly. Then you can do stuff like this:

my $thread = async {
    print "you can pass a block of code as an arg unlike Python :p";
    return some_func();
};
my $result = $thread->join();

I've definitely implemented callbacks from an event loop in an async process using forks and I don't see why it wouldn't work with threads.



回答3:

Twisted also uses cooperative multi-tasking just like POE & Coro.

However it looks like Twisted Deferred does (or can) make use of threads. NB. See this answer from the SO question Twisted: Making code non-blocking

So you would need to go the same route with POE (though using fork is probably preferable).

So one POE solution would be to use: POE::Wheel::Run - portably run blocking code and programs in subprocesses.

For alternatives to POE take a look at AnyEvent and Reflex.



回答4:

I believe you use select for that kind of thing. More similarly to forking, there's threading.



回答5:

POE is fine if you want asynchronous processing but using only a single cpu (core) is fine. For example if the app is I/O limited a single process will be enough most of the time.



回答6:

No other callback can start running while another is already running!

As far as I can tell - this is the same with all languages (per CPU thread of course; modern web servers usually spawn a least one process or thread per CPU core, so it will look (to users) like stuff it working in parallel, but the long-running callback didn't get interrupted, some other core just did that work).

You can't interrupt an interrupt, unless the interrupted interrupt has been programmed specifically to accommodate it.

Imagine code that takes 1min to run, and a PC with 16 cores - now imagine a million people try to load that page, you can deliver working results to 16 people, and "time out" all the rest, or, you can crash your web server and give no results to anyone. Folks choose not to crash their web server, which is why they never permit callbacks to interrupt other callbacks (not that they could even if they tried - the caller never gets control back to make a new call before the prior one has ended anyhow...)