2016-04-14 6 views
2

をデッドロック:dispatch_barrier_syncは、常に次のコードスニペットを考える

#import <XCTest/XCTest.h> 

@interface DispatchTests : XCTestCase { 
    dispatch_queue_t _workQueue; 
    dispatch_queue_t _readWriteQueue; 
    int _value; 
} 
-(void)read; 
-(void)write; 
@end 

@implementation DispatchTests 

-(void)testDispatch { 
    _workQueue = dispatch_queue_create("com.work", DISPATCH_QUEUE_CONCURRENT); 
    _readWriteQueue = dispatch_queue_create("com.readwrite", DISPATCH_QUEUE_CONCURRENT); 
    _value = 0; 
    for(int i = 0; i < 100; i++) { 
     dispatch_async(_workQueue, ^{ 
      if(arc4random() % 4 == 0) { 
       [self write]; 
      } else { 
       [self read]; 
      } 
     }); 
    } 
    XCTestExpectation* expectation = [self expectationWithDescription:@"dude"]; 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     [expectation fulfill]; 
    }); 

    [self waitForExpectationsWithTimeout:6.0 handler:nil]; 
} 

-(void)read { 
    dispatch_sync(_readWriteQueue, ^{ 
     NSLog(@"read:%d", _value); 
    }); 
} 

-(void)write { 
    dispatch_barrier_sync(_readWriteQueue, ^{ 
     _value++; 
     NSLog(@"write:%d", _value); 
    }); 
} 

@end 

この試験の目的は、私は、読み取り/書き込みロックを管理するためにdispatch_barrierを使用できるかどうかを確認することです。このテストでは、リーダーとライターの両方が同期しています。私がバリアを非同期にすると、テストはうまくいくように見えますが、非同期動作を避けたいのですが、この実装は簡単ではないためです。

私はwriteメソッドがデッドロックしている理由を理解しようとしています。

バリアブロックがプライベートコンカレント キューの前面に到達するとすぐには実行されません。代わりに、キューは、現在実行中のブロックの実行が終了するまで まで待機します。その時点で、 キューはバリアブロックを単独で実行します。 の後に提出されたブロックは、バリアブロックが完了するまで実行されません。

「現在実行中のブロック」が意味するものとは混同しています。

マイ解釈の束(x)は書き込み(Y)、次に、送信取得読み出し、このシナリオでは、より多くの(Z)を読み出す:

  • (X)
  • (y)を実行します待機(X)
  • の実行から
  • (Y)ブロック(z)が行われる(X)
  • を完了するには(Y)
  • (y)を実行
  • を完了するまで - 理論的にはあなたのコードではなくブロックを行います。
  • (z)は
  • (z)を実行し、実際のテストの後、それは、
+0

私はサンプルをテストしていませんが、見てから、デッドロックする必要はありません、IMHO。 – CouchDeveloper

答えて

3

OKを完了します。

しかし、実際には - それはかもしれませんです。

使用可能なすべてのシステムスレッドが使い果たされている状況が発生しています。続行するには、コードでGCDに新しいスレッドを取得させる必要がありますが、それ以上は使用できないため、デッドロックします。

このような状況を回避するには、コードを解析して、新しいスレッドを非バインドの方法で生成する必要があります。これは、並行キューで発生することがあります。ブロックがブロックされたり、処理が完了するまでに時間がかかりすぎたり、多数のブロックがその並行キューに高頻度で送信されたりします。

for(int i = 0; i < 400; i++) { 
    usleep(1000); 
    dispatch_async(_workQueue, ^{ 
     if(arc4random() % 4 == 0) { 
      [self write]; 
     } else { 
      [self read]; 
     } 

    }); 
} 

それは定期的に終了するまでのコードが実行されることがあります。あなたはわずかな遅延を挿入する場合たとえば

、。これはもちろん、あなたの問題を解決するのではなく、その問題を示すことです。

+0

これはそうであるようです。遅れなくループカウンタを50に減らすと、うまく動作します。残念ながら、無限の状況は、 'OverlayRenderer'のためのタイルを要求する' MapKit'によって引き起こされており、私はそれを抑制する方法がわかりません。 –

+1

@TimReddy解決策は、並行キューで実行される同時ブロックの最大数を制限することです。これは 'NSOperationQueue'を使用する必要がありますが、ディスパッチキューとセマフォを使用するアプローチもあります。 – CouchDeveloper

+2

チップをありがとう!私は現在、スレッドカウントを抑制するために 'dispatch_semaphore'を使用しています。 –

関連する問題