Objective-C中当读操作发生时锁定写操作

3
我有一个ObjC类(Writer),它具有一些属性,并在主线程上将这些属性写入。然后有其他类(Reader)在主线程或后台线程上读取这些属性。
当读者从主线程读取时,它得到的是Writer类中这些属性确切的值,但当读者从后台线程读取时,显然没有保证读者会得到相同的值。以下是示例代码:
在Writer类中: - numberString是由逗号分隔的全部为1的字符串,例如1,1,1,1 - numberSum是这些1的总和,例如4
在Reader类中: 我想确保numberSum确实是numberString变量中所有1的总和。
// 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中的两个NSLog语句在主线程执行时始终相同,但在后台线程中它们有时会不同,因为在读取numberString后,在Reader中读取numberSum时,它已经被Writer中的主线程更新。

我该怎么做才能确保无论调用线程如何,都能获得正确的值?

1个回答

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

谢谢你提醒我使用dispatch_barrier。不过有一个问题,它适用于一个或两个变量,但如果您想要在不同类中分散使用相同的方法,我们是否仍然遵循相同的方法,或者我们可以有其他解决方案而不必在不同类中重复相同的操作? - tarun_sharma
2
你可以考虑任何适合你情况的方法。然而,一般的想法是同步访问可能存在竞态条件的部分。因此,如果你有几个资源分散在不同的类中,请考虑创建一个对象,该对象将为您提供对所有其他对象的访问并进行所有同步。看看外观模式。 - Sergii Martynenko Jr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接