teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

6

誤字を修正しました

2020/03/16 23:03

投稿

tatsuya6502
tatsuya6502

スコア2055

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()`をんでもらわないといけません。そのためには前回の`poll_write()`で引数にとった`Context`内にある`Waker`の`wake()`メソッドを呼ぶことでランタイムに通知する必要があります。
45
+ `Mutex`のロックに失敗して`poll_write()`から`Poll::Pending`を返したとします。その後、ロックできる状態になったら、非同期ランタイムから再度`poll_write()`をんでもらわないといけません。そのためには前回の`poll_write()`で引数にとった`Context`内にある`Waker`の`wake()`メソッドを呼ぶことでランタイムに通知する必要があります。
46
46
 
47
47
  質問の追記部分にあるコードですと、`wake()`メソッドを呼ぶことがないので、ランタイムが`poll_write()`を呼んでくれなくなってしまい、非同期タスクが前に進みません。
48
48
 

5

誤字を修正しました

2020/03/16 23:03

投稿

tatsuya6502
tatsuya6502

スコア2055

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

デッドロックに関する説明を訂正しました

2020/03/16 18:25

投稿

tatsuya6502
tatsuya6502

スコア2055

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

追記部分の説明を改良しました

2020/03/16 18:23

投稿

tatsuya6502
tatsuya6502

スコア2055

answer CHANGED
@@ -38,8 +38,10 @@
38
38
 
39
39
  **追記**
40
40
 
41
- 「追記 2020-03-17 01:20」についてですが、`tokio::sync::Mutex::try_lock()`だとダメそうな気がします。`poll_write()`から`Poll::Pending`を返したあと、ロックできる状態になったら、非同期ランタイムから再度`poll_write()`を読んでもらわないといけません。そのためには前回の`poll_write()`で引数にとった`Context`内にある、`Waker`の`wake()`メソッドを呼ぶことでランタイムに通知する必要があります。
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

「動かないようでしたら調べますので...」という一文を追加しました。

2020/03/16 17:49

投稿

tatsuya6502
tatsuya6502

スコア2055

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」にたいする回答を追加しました

2020/03/16 17:44

投稿

tatsuya6502
tatsuya6502

スコア2055

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) が参考になると思います。