improved futures chapter
This commit is contained in:
@@ -805,6 +805,60 @@ the <code>async</code> and <code>await</code> keywords.</li>
|
||||
</ol>
|
||||
<p>That's really what Rusts standard library does. As you see there is no definition
|
||||
of non-blocking I/O, how these tasks are created or how they're run.</p>
|
||||
<h2><a class="header" href="#io-vs-cpu-intensive-tasks" id="io-vs-cpu-intensive-tasks">I/O vs CPU intensive tasks</a></h2>
|
||||
<p>As you know now, what you normally write are called non-leaf futures. Let's
|
||||
take a look at this async block using pseudo-rust as example:</p>
|
||||
<pre><code class="language-rust ignore">let non_leaf = async {
|
||||
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap(); <-- yield
|
||||
|
||||
// request a large dataset
|
||||
let result = stream.write(get_dataset_request).await.unwrap(); <-- yield
|
||||
|
||||
// wait for the dataset
|
||||
let mut response = vec![];
|
||||
stream.read(&mut response).await.unwrap(); <-- yield
|
||||
|
||||
// do some CPU-intensive analysis on the dataset
|
||||
let report = analyzer::analyze_data(response).unwrap();
|
||||
|
||||
// send the results back
|
||||
stream.write(report).await.unwrap(); // <-- yield
|
||||
};
|
||||
</code></pre>
|
||||
<p>Now, as you'll see when we go through how Futures work, the code we write between
|
||||
the yield points are run on the same thread as our executor.</p>
|
||||
<p>That means that while our <code>analyzer</code> is working on the dataset, the executor
|
||||
is busy doing calculations instead of handling new requests.</p>
|
||||
<p>Fortunately there are a few ways to handle this, and it's not difficult, but it's
|
||||
something you must be aware of:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>We could create a new leaf future which sends our task to another thread and
|
||||
resolves when the task is finished. We could <code>await</code> this leaf-future like any
|
||||
other future.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The runtime could have some kind of supervisor that monitors how much time
|
||||
different tasks take, and move the executor itself to a different thread.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>You can create a reactor yourself which is compatible with the runtime which
|
||||
does the analysis any way you see fit, and returns a Future which can be awaited.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Now #1 is the usual way of handling this, but some executors implement #2 as well.
|
||||
The problem with #2 is that if you switch runtime you need to make sure that it
|
||||
supports this kind of supervision as well or else you will end up blocking the
|
||||
executor.</p>
|
||||
<p>#3 is more of theoretical importance, normally you'd be happy by sending the task
|
||||
to the thread-pool most runtimes provide.</p>
|
||||
<p>Most executors have a way to accomplish #1 using methods like <code>spawn_blocking</code>.</p>
|
||||
<p>These methods send the task to a thread-pool created by the runtime where you
|
||||
can either perform CPU-intensive tasks or "blocking" tasks which is not supported
|
||||
by the runtime.</p>
|
||||
<p>Now, armed with this knowledge you are already on a good way for understanding
|
||||
Futures, but we're not gonna stop yet, there is lots of details to cover. Take a
|
||||
break or a cup of coffe and get ready as we go for a deep dive in the next chapters.</p>
|
||||
<h2><a class="header" href="#bonus-section" id="bonus-section">Bonus section</a></h2>
|
||||
<p>If you find the concepts of concurrency and async programming confusing in
|
||||
general, I know where you're coming from and I have written some resources to
|
||||
|
||||
Reference in New Issue
Block a user