質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2326閲覧

swiftのsemaphoreを使うと、完了ハンドラ内の処理が実行されなくなる

oeiqgfodgfhps

総合スコア35

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/08/16 13:56

編集2021/08/17 13:46

お世話になっております。
seiftでMoyaを利用したhttp通信の結果を返すために、semaphoreを使い、非同期処理を同期的に処理するようなプログラムを実装しています。
しかし、ApiClientのrequestメソッドの完了ハンドラに書いてある処理が実行されません。
semaphoreを無くしたら動いたので、おそらくsemaphoreの使用方法がまずいのだと思いますが、どのように改善していけばいいでしょうか

試したこと

この記事に同じような問題の解決策が載っていましたが、これを参考にコードを変更してもうまく動きませんでした。

swift

1 func isLogin() -> Bool { 2 var isLogin = false 3 let semaphore = DispatchSemaphore(value: 0) 4 DispatchQueue.global(qos: .default).async { 5 ApiClient.shared.request(CheckTokenTargetType()) { result in 6 switch result { 7 case .success(_): 8 isLogin = true 9 10 default: 11 break 12 } 13 semaphore.signal() 14 } 15 } 16 semaphore.wait() 17 return isLogin 18 }

swift

1class ApiClient: ApiClientInterface { 2 private init() {} 3 4 static let shared = ApiClient() 5 6 func request<T>(_ request: T, completion: @escaping (Result<T.Response, MoyaResponseError>) -> Void) where T: ApiTargetType { 7 let provider = MoyaProvider<T>() 8 provider.request(request) { result in 9 switch result { 10 case let .success(response): 11 if let model = try? response.map(T.Response.self) { 12 completion(.success(model)) 13 } else { 14 completion(.failure(.unknownError)) 15 } 16 17 case let .failure(moyaError): 18 completion(.failure(moyaError)) 19 } 20 } 21 } 22}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2021/08/16 19:32

非同期処理は非同期処理として処理すべきだと思います。
oeiqgfodgfhps

2021/08/17 01:00

非同期処理で処理すると非同期処理の結果を戻り値として返せないと思うのですが、、、 非同期処理でもその結果を呼び出し元の関数が戻り値を返す前に返す方法などありますか??
hoshi-takanori

2021/08/17 02:48

非同期処理は、例えば Web API なら、ネットワークを通してサーバーにリクエストを送って、サーバー上で何か処理をして、結果が返ってくるわけで、どうしても時間がかかりますし、どのくらい時間がかかるかもわからない (サーバーの障害で返事が返らない可能性もあります) わけで、同期的に処理するということは、その間スレッドを停止して結果を待つことになります。特に、メインスレッドの場合はその間 GUI を止めて待つことになりますから、ユーザーから見るとアプリが固まったように見えてしまいます。 非同期処理というのは、リクエストを出したらすぐに制御を戻して、結果が返るまで例えばローディング中の表示にして待ち、結果が返ってきたらクロージャが呼ばれるのでそこで処理するという形になります。
oeiqgfodgfhps

2021/08/17 12:45

アドバイスありがとうございます! そのように実装していこうと思います。 一つ疑問なのですが、なぜ自分が投稿したコードでは非同期の部分が呼ばれないのでしょうか??
hoshi-takanori

2021/08/17 13:08

ApiClient.shared.request(CheckTokenTargetType()) { result in 〜 } は、API リクエストを送信したらすぐに戻って semaphore.wait() します。そして、API の結果が返った時に { result in 〜 } の部分が実行されるはずですが、Moya のデフォルトの設定ではどうやらメインスレッドで実行することになってるようなので、isLogin メソッドの終了後に実行されるようにスケジュールされますが、isLogin メソッドは semaphore.wait() で止まってるために semaphore.signal() は実行されず、デッドロックすると思います。
oeiqgfodgfhps

2021/08/17 13:21

ということはMoyaのリクエストの処理をバックグラウンドスレッドで行うようにすればsemaphore.signal()は実行されるという認識でよかったですか?
hoshi-takanori

2021/08/17 13:38

そうですね。バックグラウンドスレッドで UI 操作などをしなければ大丈夫だと思います。
oeiqgfodgfhps

2021/08/17 13:47

ありがとうございます!! バックグラウンドスレッドで実行するようにコードを変更してみました(コードを編集しました)が、状況は変化しませんでした。。 何かおかしなところはありますでしょうか??
guest

回答1

0

ベストアンサー

Moya はデフォルトでは completion クロージャをメインスレッドで呼び出すようなので、semaphore がデッドロックしてしまいます。これを防ぐには、ApiClient.shared.request ではなく、completion クロージャをバックグラウンドスレッドで呼び出すように Moya にお願いする必要があります。
参考: Support Synchronous requests for SPM CLI tools · Issue #1562 · Moya/Moya

diff

1- provider.request(request) { result in 2+ provider.request(request, callbackQueue: .global(qos: .default)) { result in

とはいえ、API の返事をメインスレッドで待つと GUI が固まってしまうので、ちゃんと非同期処理で書くことをお勧めします。

投稿2021/08/17 16:40

hoshi-takanori

総合スコア7901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

oeiqgfodgfhps

2021/08/18 06:46

ありがとうございます! 一点質問させてください。 Moya はデフォルトでは completion クロージャをメインスレッドで呼び出すというのは、リクエスト自体はバックグラウンドスレッドで行うが、完了ハンドラはメインスレッドという認識をしています。 なので、semaphoreでメインスレッドを止めている場合でも、リクエスト自体はバックグラウンドスレッドなので行われるが、完了ハンドラはメインスレッドだからデッドロックしてしまうという認識であっていますか??
hoshi-takanori

2021/08/18 07:48

いえ、リクエストはメインスレッドのままで構いません。リクエストの送信が完了してから semaphore.wait() してますので。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問