2

スレッド AI 内で、スレッド B で実行される非同期サービスを呼び出します。サービスは、終了後にデリゲート メソッドを呼び出します。スレッド B が終了するまでスレッド A を待機させます。これには NSCondition を使用しました。

これは私のセットアップです(重要でないものをスキップしました):

-(void)load
{
    self.someCheckIsTrue = YES;
    self.condition = [[NSCondition alloc] init];
    [self.condition lock];

    NSLog(@"log1");
    Service *service = // set up service
    [service request:url delegate:self didFinishSelector:@selector(response:)];

    while (self.someCheckIsTrue)
        [self.condition wait];

    NSLog(@"log3");
    [self.condition unlock];
}

-(void)response:(id)data
{
    NSLog(@"log2");
    [self.condition lock];
    self.someCheckIsTrue = NO;

    // do something with the response, doesn't matter here

    [self.condition signal];
    [self.condition unlock];
} 

何らかの理由で、「log1」のみが出力され、「log2」も「log3」も出力されません。デリゲート メソッドの応答がスレッド B である「サービス スレッド」によって呼び出されるのに対し、ロードはスレッド A によって呼び出されるのはそのためだと思います。

セマフォも試しましたが、どちらも機能しません。コードは次のとおりです。

-(void)load
{        
    NSLog(@"log1");
    Service *service = // set up service

    self.sema = dispatch_semaphore_create(0);
    [service request:url delegate:self didFinishSelector:@selector(response:)];
    dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);

    NSLog(@"log3");
}

-(void)response:(id)data
{
    NSLog(@"log2");

    // do something with the response, doesn't matter here

    dispatch_semaphore_signal(self.sema);
} 

どうすればこれを機能させることができますか?

4

4 に答える 4

2

いくつかの問題があるようです:

  1. あなたのNSCondition例では、ロックを行っており、状態変数を設定loadするまでロックを解除しません。responseただしresponse、ロックも実行しようとしているため、これに到達することはできません (ロックの性質上、他のスレッドがロックを解放するまでブロックされます)。

  2. さらに、リクエストloadを開始しserviceています(その詳細は私たちと共有していません)が、あなたが説明した動作(つまり、「log2」が表示されない)に基づいて、このサービスリクエストは同じ上で実行されるようにスケジュールされていると推測していますスレッドとしてloadload(サービスが他のrunloop/queue/thread で実行されている場合、そのサービスの開始時にこれを明示する引数が表示されることがよくあります。)サービスが開始されません。serviceそれについてさらにコメントするには、リクエストの性質の詳細を共有する必要があります.

  3. コメントでは、 を使用して説明しGDataServiceGoogleました。最初の質問で、このサービスは別のスレッドで実行されていると提案しました。しかし、彼らのサンプル アプリの 1 つを調べたところ、NSURLConnectionDataDelegateメソッドにブレークポイントを設定したところ、メイン スレッドで呼び出されていました (実際には、「現在の」スレッドを使用しており、サンプルがメイン スレッドから開始したため、NSURLConnectionDataDelegate呼び出しはメインスレッドで行われました) 。これは私の以前のポイントを確認します。

    (ちなみに、これはまったく珍しいことではありません。多くのNSURLConnectionDataDelegateベース実装では、ネットワーク接続にメイン キューを使用します。私はその方法に夢中ではありませんが、ネットワーク アクティビティ用に別の実行ループを作成する必要がなくなります。メインキューをブロックしないと仮定して.)

    ただし、サービスを呼び出したスレッドにロックまたはセマフォがある場合、NSURLConnectionDataDelegateメソッドが呼び出されなくなり、response渡されたメソッドdidFinishSelectorが呼び出されることはありません。デッドロック。

  4. しかし、別の問題を特定したようです。それは、あなたからサービス呼び出しを開始するNSOperationと、サービスの内部NSURLConnectionDataDelegate呼び出しが呼び出されないということです。これは、バックグラウンド キューからの呼び出しでよくある問題でありNSURLConnection、通常は次のいずれかによって解決されます。(a) 独自の実行ループを持つ専用スレッドでネットワーク接続をスケジュールする。(b)NSURLConnectionでのスケジューリング[NSRunLoop mainRunLoop]。または (c) 操作用に独自の実行ループを作成します。そして、この GDataServiceGoogle サービスはどの実行ループを使用するかを制御するためのインターフェースを公開していないため、オプション (c) を選択する必要があることを確認できました。これはおそらく最も洗練されていないソリューションですが、その GDataServiceGoogle の制約を考えると、これが最善の方法かもしれません。

あなたが尋ねる:

どうすればこれを機能させることができますか?

while以下にいくつかの解決策を説明しますが、最も重要な点は、セマフォ、ロック、またはタイトループをまったく使用してはならないということです。これらはすべて、これらの非同期リクエストを処理する正しい方法についての誤解を表しています。サービス リクエストが完了するのを「待つ」のではなく、完了すると (responseメソッドが呼び出されるときに) 通知されます。セマフォ、ロック、およびタイトなwhileループをすべて削除し、「log3」で実行したいロジックを移動して、responseメソッド内に配置します。

それを踏まえて、デッドロックをより一般的に考えると、いくつかの観察結果があります。

  1. ロックされないスレッドでサービスをスケジュールしてください。たとえば、ネットワーク サービスが実行される 3 番目のサービス専用スレッド/キューがよく見られます。または、メイン スレッド (ブロックしない) でネットワーク関連のスケジュールを設定する人もいますが、私はこの種の専用スレッドを好みます。または、ルーチン内で runloop を実行するための呼び出しを実際に行っている人もいloadます (しかし、これは恐ろしい慣行だと思います)。ただし、loadブロッキング待機または他の機能を実行して、同じスレッドでサービスを実行することはできません。そのため、サービスが独自の実行ループを持つ他のスレッドで実行されていることを確認してください。

  2. ロックを使用してキー変数へのアクセスを同期する場合は、どちらのスレッドも正当な理由なく長時間ロックを保持していないことを確認してください。ロックの期間 (存在する場合) をコードの可能な限り最小の部分に最小限に抑えるようにしてください。つまり、相互にアクセスするリソースを更新する必要があるときだけですが、そのロックはできるだけ早く解放してください。dispatch_semaphore_waitしかし、または永久whileループのようなものをロックすることは、多くの場合問題になります。

  3. もっと過激なことに、コードをリファクタリングして、ロックとセマフォを完全に排除する可能性があるかどうかを尋ねるかもしれません。(コンカレンシー プログラミング ガイドの「ロックベースのコードの削除」を参照してください。 ) 実用的でない場合もありますが、シリアル キュー (またはコンカレント キューのバリア) により、以前はロックとセマフォに依存していた多くの状況が解消されました。

上で述べたように、正しい解決策は、「サービスが終了するのを待つ」モデルから離れて、サービスが終了したresponseときにメソッドを呼び出すことに依存することだと思います。デッドロックの問題がなくなり、コードの効率が大幅に向上します。

于 2013-08-24T04:55:51.390 に答える