3

プロパティを持つObjCクラス(Writer)があり、メインスレッドのこれらのプロパティに書き込みます。次に、メインスレッドまたはバックグラウンドスレッドでこれらのプロパティを読み取る他のクラス(Reader)があります。ObjectiveCは読み取り時に書き込み操作を実行します

リーダーがメインスレッドから読み取るとき、Writerクラスのこれらのプロパティが保持する正確な値を取得しますが、リーダーがバックグラウンドスレッドから読み取るときには、明らかに同じ値を保証する保証はありません。ここに例のコードを示します。

ここライター・クラスの

  • numberStringは、カンマで区切られたすべてのもの、例です。 1,1,1,1
  • numberSumはこれらの合計です。リーダークラスで4

私はnumberSumが実際に numberString変数内のすべてのものの和であることを確認します。

// Writer Class 
@interface Writer : NSObject 
@property (nonatomic,strong) NSString* numberString; 
@property (nonatomic,assign) NSUInteger numberSum; 
@end 

@implementation Writer 

-(instancetype)init { 
    if (self = [super init]){ 
     self.numberString = @"1"; 
     self.numberSum = 1; 

     self.timer = [NSTimer timerWithTimeInterval:0.1f target:self selector:@selector(writeToVariables:) userInfo:nil repeats:YES]; 
     [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; 
    } 

    return self; 
} 

- (void)writeToVariables:(NSTimer *)timer { 
    self.numberString = [self.numberString stringByAppendingString:@",1"];  
    self.numberSum += 1; 
} 

// Reader Class 
@interface Reader() 
@property (nonatomic,strong) Writer* writer; 
@end 

@implementation Reader 

-(instancetype)init { 
     if (self = [super init]){ 
      self.writer = [Writer new]; 
     } 
     return self; 
    } 

-(void) getVariablesOnBackground:(BOOL)background { 

    void (^readerBlock)() = ^{ 

     NSString* numberStr = self.writer.numberString; 

     // Introduce delay So background thread get time to update numberSum 
     int i=0; 
     while (i < 10000000) { 
      i += 1; 
     } 

     NSUInteger numberSum = self.writer.numberSum; 

     NSArray *onesArray = [numberStr componentsSeparatedByString:@","]; 
     NSLog(@"%lu",(unsigned long)onesArray.count); 
     NSLog(@"%lu",(unsigned long)numberSum); 
    }; 

    // If background then dispatch on default queue else just call the block 
    if (background) { 
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),readerBlock); 
    } 
    else { 
     readerBlock(); 
    } 
} 
@end 

これは単なる例示のためのデモコードです。 readerBlock内の2つのNSLogステートメントは、メインスレッドで実行されたときに常に同じものが印刷されますが、バックグラウンドスレッドでは、WriterのメインスレッドによってnumStringが読み取られた後、Readerで読み取られるtimeSumによって時間が異なります。

呼び出しスレッドに関係なく常に正しい値を取得するにはどうすればよいですか?

答えて

1

必要なものは、並行ディスパッチキューとディスパッチバリア機能の組み合わせです。長い間、numberStringnumberSumの両方に同時にアクセスする必要がある場合は、非表示にして、両方を取得する単一の方法を提供する必要があります。このように

@interface Writer : NSObject { 
    NSString* numberString; 
    NSUInteger numberSum; 

    dispatch_queue_t accessQueue; 
} 

- (NSDicionary*) readStringAndSum; 
@end 

@implementation Writer 

- (instancetype) init { 
    ... 
    accessQueue = dispatch_queue_create("Access queue", DISPATCH_QUEUE_CONCURRENT); 
    ... 
} 

- (void)writeToVariables:(NSTimer *)timer { 

    dispatch_barrier_async(accessQueue, ^{ 
      numberString = [self.numberString stringByAppendingString:@",1"];  
      numberSum += 1; 
     }); 

} 


- (NSDicionary*) readStringAndSum; { 
    __block NSDictionary* response; 
    dispatch_sync(accessQueue ^{ 
     response = @{@"Sum" : [NSNumber numberWithUnsignedInteger: numberSum], 
        @"String" : [numberString copy]; 
        } 
    }); 

    return response; 
} 

@end 
+0

私はdispatch_barrierを思い出してくれてありがとう。しかし、1つまたは2つの変数ではうまくいきますが、異なるクラスに分散したかなりの変数で同じものを使用したい場合はどうすればよいでしょうか。私たちはまだ同じアプローチに従っていますか、あるいは別のクラスで同じことをしなくても、他の解決策をとることはできますか? –

+2

あなたのケースではうまくいくようなアプローチが考えられます。しかし、一般的な考え方は、競合状態が可能なセクションへのアクセスを同期させることです。したがって、クラスに分散するリソースがほとんどない場合は、他のすべてのクラスにアクセスし、すべての同期を行うオブジェクトを作成することを検討してください。ファサードのパターンを見てください –

関連する問題