回答編集履歴
6
誤字を修正しました
answer
CHANGED
@@ -42,7 +42,7 @@
|
|
42
42
|
|
43
43
|
「追記 2020-03-17 01:20」についてですが、`tokio::sync::Mutex::try_lock()`だとダメそうな気がします。
|
44
44
|
|
45
|
-
`Mutex`のロックに失敗して`poll_write()`から`Poll::Pending`を返したとします。その後、ロックできる状態になったら、非同期ランタイムから再度`poll_write()`を
|
45
|
+
`Mutex`のロックに失敗して`poll_write()`から`Poll::Pending`を返したとします。その後、ロックできる状態になったら、非同期ランタイムから再度`poll_write()`を呼んでもらわないといけません。そのためには前回の`poll_write()`で引数にとった`Context`内にある`Waker`の`wake()`メソッドを呼ぶことでランタイムに通知する必要があります。
|
46
46
|
|
47
47
|
質問の追記部分にあるコードですと、`wake()`メソッドを呼ぶことがないので、ランタイムが`poll_write()`を呼んでくれなくなってしまい、非同期タスクが前に進みません。
|
48
48
|
|
5
誤字を修正しました
answer
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
// メソッドを呼ぶ。poll()のレシーバーはPin<&mut self>。
|
19
19
|
// lock()はimpl Future<Output = MutexGuard<'_, T>>を返すので
|
20
20
|
// FutureExtトレイトのboxed()でPin<Box<dyn Future<Output = ..>>に
|
21
|
-
// 変換し、as_mut()でPin<&mut Future<Output = ..>>に変換すればよい。
|
21
|
+
// 変換し、as_mut()でPin<&mut dyn Future<Output = ..>>に変換すればよい。
|
22
22
|
match self.0.lock().boxed().as_mut().poll(cx) {
|
23
23
|
Poll::Pending => Poll::Pending,
|
24
24
|
Poll::Ready(mut s) => {
|
4
デッドロックに関する説明を訂正しました
answer
CHANGED
@@ -34,8 +34,10 @@
|
|
34
34
|
|
35
35
|
> 後述の通り、代わりに `std::sync::Mutex` を用いた実装はできたので `tokio::sync::Mutex` が使えない場合はこちらを使おうと思うのですが、その場合のパフォーマンスへの影響はどのようなものでしょうか?
|
36
36
|
|
37
|
-
tokioの`Runtime`のようなマルチスレッドな非同期ランタイムでは、非同期タスクに対応していない`Mutex`は使えないようです。[こちら](https://rust-lang.github.io/async-book/03_async_await/01_chapter.html?highlight=mutex#awaiting-on-a-multithreaded-executor) の説明によると、デッドロックを引き起こす恐れがあるそうです。
|
37
|
+
~~tokioの`Runtime`のようなマルチスレッドな非同期ランタイムでは、非同期タスクに対応していない`Mutex`は使えないようです。[こちら](https://rust-lang.github.io/async-book/03_async_await/01_chapter.html?highlight=mutex#awaiting-on-a-multithreaded-executor) の説明によると、デッドロックを引き起こす恐れがあるそうです。~~
|
38
38
|
|
39
|
+
(訂正)参照先の説明をもう一度読んだところ、デッドロックを引き起こす可能性があるのは、複数の`.await`にまたがってロックを使用するときだけだとわかりました。今回の使いかたは、それには該当しないはずです。ただデッドロックは起こらなくても、stdの`Mutex`の`lock()`はブロッキングな操作ですので、非同期タスク内から呼ぶと非同期ランタイムのexecutorスレッドの実行を一時的にブロックします。ですから性能面の影響は多少あるかもしれません。それほど長い時間はブロックしなさそうなので、無視できる範囲かもしれませんが。
|
40
|
+
|
39
41
|
**追記**
|
40
42
|
|
41
43
|
「追記 2020-03-17 01:20」についてですが、`tokio::sync::Mutex::try_lock()`だとダメそうな気がします。
|
3
追記部分の説明を改良しました
answer
CHANGED
@@ -38,8 +38,10 @@
|
|
38
38
|
|
39
39
|
**追記**
|
40
40
|
|
41
|
-
「追記 2020-03-17 01:20」についてですが、`tokio::sync::Mutex::try_lock()`だとダメそうな気がします。
|
41
|
+
「追記 2020-03-17 01:20」についてですが、`tokio::sync::Mutex::try_lock()`だとダメそうな気がします。
|
42
42
|
|
43
|
+
`Mutex`のロックに失敗して`poll_write()`から`Poll::Pending`を返したとします。その後、ロックできる状態になったら、非同期ランタイムから再度`poll_write()`を読んでもらわないといけません。そのためには前回の`poll_write()`で引数にとった`Context`内にある`Waker`の`wake()`メソッドを呼ぶことでランタイムに通知する必要があります。
|
44
|
+
|
43
45
|
質問の追記部分にあるコードですと、`wake()`メソッドを呼ぶことがないので、ランタイムが`poll_write()`を呼んでくれなくなってしまい、非同期タスクが前に進みません。
|
44
46
|
|
45
47
|
回答に書いたコードでは`lock()`が返した`Future`の`poll()`で`Context`を引数として渡しています。こうすることで、ロックできる状態になったときに、tokioの`Mutex`の実装側から`wake()`を呼んでもらえます。
|
2
「動かないようでしたら調べますので...」という一文を追加しました。
answer
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
`async fn`の戻り値型が`T`のとき、その関数を呼ぶと`impl Future<Output = T>`の値が返されます。ですから`.await`が使えない状況でも、`Future`の`poll()`メソッドを呼ぶことで`Future`を駆動できます。
|
4
4
|
|
5
|
-
`poll_write()`の実装を以下のようにしたところコンパイルは通りました。ただし、私は`Future`トレイトや`AsyncWrite`トレイトを実装するのは不慣れですので、これで正しく動くという自信はありません。
|
5
|
+
`poll_write()`の実装を以下のようにしたところコンパイルは通りました。ただし、私は`Future`トレイトや`AsyncWrite`トレイトを実装するのは不慣れですので、これで正しく動くという自信はありません。動かないようでしたら調べますので教えてください。
|
6
6
|
|
7
7
|
```rust
|
8
8
|
use futures::future::FutureExt; // Cargo.toml: futures = "0.3"
|
1
質問の「追記 2020-03-17 01:20」にたいする回答を追加しました
answer
CHANGED
@@ -34,4 +34,14 @@
|
|
34
34
|
|
35
35
|
> 後述の通り、代わりに `std::sync::Mutex` を用いた実装はできたので `tokio::sync::Mutex` が使えない場合はこちらを使おうと思うのですが、その場合のパフォーマンスへの影響はどのようなものでしょうか?
|
36
36
|
|
37
|
-
tokioの`Runtime`のようなマルチスレッドな非同期ランタイムでは、非同期タスクに対応していない`Mutex`は使えないようです。[こちら](https://rust-lang.github.io/async-book/03_async_await/01_chapter.html?highlight=mutex#awaiting-on-a-multithreaded-executor) の説明によると、デッドロックを引き起こす恐れがあるそうです。
|
37
|
+
tokioの`Runtime`のようなマルチスレッドな非同期ランタイムでは、非同期タスクに対応していない`Mutex`は使えないようです。[こちら](https://rust-lang.github.io/async-book/03_async_await/01_chapter.html?highlight=mutex#awaiting-on-a-multithreaded-executor) の説明によると、デッドロックを引き起こす恐れがあるそうです。
|
38
|
+
|
39
|
+
**追記**
|
40
|
+
|
41
|
+
「追記 2020-03-17 01:20」についてですが、`tokio::sync::Mutex::try_lock()`だとダメそうな気がします。`poll_write()`から`Poll::Pending`を返したあと、ロックできる状態になったら、非同期ランタイムから再度`poll_write()`を読んでもらわないといけません。そのためには前回の`poll_write()`で引数にとった`Context`内にある、`Waker`の`wake()`メソッドを呼ぶことでランタイムに通知する必要があります。
|
42
|
+
|
43
|
+
質問の追記部分にあるコードですと、`wake()`メソッドを呼ぶことがないので、ランタイムが`poll_write()`を呼んでくれなくなってしまい、非同期タスクが前に進みません。
|
44
|
+
|
45
|
+
回答に書いたコードでは`lock()`が返した`Future`の`poll()`で`Context`を引数として渡しています。こうすることで、ロックできる状態になったときに、tokioの`Mutex`の実装側から`wake()`を呼んでもらえます。
|
46
|
+
|
47
|
+
`Waker`の働きについては公式のAsync Bookの [この章](https://rust-lang.github.io/async-book/02_execution/03_wakeups.html) が参考になると思います。
|