Ios Objective-C中原子/非原子的证据

Ios Objective-C中原子/非原子的证据,ios,objective-c,macos,atomic,Ios,Objective C,Macos,Atomic,阅读后,我试图在Objective-C中证明属性的原子性或非原子性。为此,我创建了一个名为Person的类 人 @interface Person : NSObject @property (nonatomic, strong) NSString *firstName; @property (nonatomic, strong) NSString *lastName; - (instancetype)initWithFirstName:(NSString *)fn lastName:(NSSt

阅读后,我试图在Objective-C中证明属性的原子性或非原子性。为此,我创建了一个名为Person的类

@interface Person : NSObject
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;

- (instancetype)initWithFirstName:(NSString *)fn lastName:(NSString *)ln;
@end

@implementation Person

- (instancetype)initWithFirstName:(NSString *)fn lastName:(NSString *)ln {
    if (self = [super init]) {
        self.firstName = fn;
        self.lastName = ln;
    }
    return self;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

@end
在另一个类中,这里是我的AppDelegate,我有一个非原子属性,它是Person的实例

@property (strong, nonatomic) Person *p;
在实现文件中,我创建了三个并发队列。在第一个队列中,我读取属性,在另外两个队列中,我写入不同的person值

据我所知,我的日志中可以有Bob Frost或Jack Sponge输出,因为我声明我的属性为非原子。但那没有发生。我不明白为什么。我是错过了什么还是误解了什么

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

    Person *bob = [[Person alloc] initWithFirstName:@"Bob" lastName:@"Sponge"];
    Person *jack = [[Person alloc] initWithFirstName:@"Jack" lastName:@"Frost"];
    self.p = bob;

    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue3 = dispatch_queue_create("queue3", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue1, ^{
        while (YES) {
            NSLog(@"%@", self.p);
        }
    });

    dispatch_async(queue2, ^{
        while (YES) {
            self.p = bob;
        }
    });

    dispatch_async(queue3, ^{
        while (YES) {
            self.p = jack;
        }
    });

    return YES;
}

拥有非原子属性使部分写入成为可能,但这一点并不确定

在Person类中,设置名字和姓氏的唯一方法是在init方法中,然后立即设置名字和姓氏。设置名字和姓氏将彼此非常接近,几乎不会有另一个线程在操作之间把事情搞砸

此外,在运行并发操作之前,在主线程中创建Person对象。在当前代码运行时,对象已经存在,并且您不再更改它们的名称值,因此不可能出现争用条件或部分写入名称值。您只是在两个对象之间更改self.p,这两个对象创建后不会更改

也就是说,代码中不可预测的是self.p中的person对象在任何时刻都是什么。您应该会看到显示的值在Bob Spong和Jack Frost之间交替出现,这是不可预测的

更好的测试应该是这样的:

#include <stdlib.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
  dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
  dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

  self.thing1 = [[TestObject alloc] init];
  self.thing2 = [[TestObject alloc] init];

  dispatch_async(queue1, ^
  {
    for (int x = 0; x < 100; x++) 
    {
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      int thing1Val = arc4random_uniform(10000);
      int thing2Val = arc4random_uniform(10000);
      _thing1.x1 = thing1Val;
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x1 = thing2Val;
      _thing1.x2 = thing1Val; //thing1's x1 and x2 should now match
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x2 = thing2Val; //And now thing2's x1 and x2 should also both match
    }
  });


  //Do the same thing on queue2
  dispatch_async(queue2, ^
  {
    for (int x = 0; x < 100; x++) 
    {
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      int thing1Val = arc4random_uniform(10000);
      int thing2Val = arc4random_uniform(10000);
      _thing1.x1 = thing1Val;
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x1 = thing2Val;
      _thing1.x2 = thing1Val; //thing1's x1 and x2 should now match
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x2 = thing2Val; //And now thing2's x1 and x2 should also both match
    }
  });

  //Log the values in thing1 and thing2 every .1 second
  self.aTimer = [NSTimer scheduledTimerWithTimeInterval:.1
    target:self
    selector:@selector(logThings:)
    userInfo:nil
    repeats:YES];

  //After 5 seconds, kill the timer.
  self.secondTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
    target:self
    selector:@selector(stopRepeatingTimer:)
    userInfo:nil
    repeats:NO];
  return YES;
}

- (void)stopRepeatingTimer:(NSTimer *)timer 
{
  [self.aTimer invalidate];
}

- (void)logThings:(NSTimer *)timer 
{
  NSString *equalString;
  if (_thing1.x1 == _thing1.x2) 
  {
    equalString = @"equal";
  }
    else 
  {
    equalString = @"not equal";
  }
  NSLog(@"%@ : thing1.x1 = %d, thing1.x2 = %d", 
    equalString, 
    _thing1.x1, 
    _thing1.x2);

  if (_thing2.x1 == _thing2.x2) 
    {
      equalString = @"equal";
    }
  else 
    {
      equalString = @"not equal";
    }
  NSLog(@"%@ : thing2.x1 = %d, thing2.x2 = %d", 
    equalString, 
    _thing2.x1, 
    _thing2.x2);
 }
(假设每个测试对象的x1和x2值应始终保持不变。)

然后编码如下:

#include <stdlib.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
  dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
  dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

  self.thing1 = [[TestObject alloc] init];
  self.thing2 = [[TestObject alloc] init];

  dispatch_async(queue1, ^
  {
    for (int x = 0; x < 100; x++) 
    {
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      int thing1Val = arc4random_uniform(10000);
      int thing2Val = arc4random_uniform(10000);
      _thing1.x1 = thing1Val;
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x1 = thing2Val;
      _thing1.x2 = thing1Val; //thing1's x1 and x2 should now match
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x2 = thing2Val; //And now thing2's x1 and x2 should also both match
    }
  });


  //Do the same thing on queue2
  dispatch_async(queue2, ^
  {
    for (int x = 0; x < 100; x++) 
    {
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      int thing1Val = arc4random_uniform(10000);
      int thing2Val = arc4random_uniform(10000);
      _thing1.x1 = thing1Val;
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x1 = thing2Val;
      _thing1.x2 = thing1Val; //thing1's x1 and x2 should now match
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x2 = thing2Val; //And now thing2's x1 and x2 should also both match
    }
  });

  //Log the values in thing1 and thing2 every .1 second
  self.aTimer = [NSTimer scheduledTimerWithTimeInterval:.1
    target:self
    selector:@selector(logThings:)
    userInfo:nil
    repeats:YES];

  //After 5 seconds, kill the timer.
  self.secondTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
    target:self
    selector:@selector(stopRepeatingTimer:)
    userInfo:nil
    repeats:NO];
  return YES;
}

- (void)stopRepeatingTimer:(NSTimer *)timer 
{
  [self.aTimer invalidate];
}

- (void)logThings:(NSTimer *)timer 
{
  NSString *equalString;
  if (_thing1.x1 == _thing1.x2) 
  {
    equalString = @"equal";
  }
    else 
  {
    equalString = @"not equal";
  }
  NSLog(@"%@ : thing1.x1 = %d, thing1.x2 = %d", 
    equalString, 
    _thing1.x1, 
    _thing1.x2);

  if (_thing2.x1 == _thing2.x2) 
    {
      equalString = @"equal";
    }
  else 
    {
      equalString = @"not equal";
    }
  NSLog(@"%@ : thing2.x1 = %d, thing2.x2 = %d", 
    equalString, 
    _thing2.x1, 
    _thing2.x2);
 }
#包括
-(BOOL)应用程序:(UIApplication*)应用程序使用选项完成启动:(NSDictionary*)启动选项
{
调度队列1=调度队列创建(“队列1”,调度队列并发);
调度队列2=调度队列创建(“队列2”,调度队列并发);
self.thing1=[[TestObject alloc]init];
self.thing2=[[TestObject alloc]init];
调度异步(队列1^
{
对于(int x=0;x<100;x++)
{
usleep(arc4random_uniform(50000));//睡眠0到50k微秒
int thing1Val=arc4random_均匀(10000);
int thing2Val=arc4random_均匀(10000);
_thing1.x1=thing1Val;
usleep(arc4random_uniform(50000));//睡眠0到50k微秒
_thing2.x1=thing2Val;
_thing1.x2=thing1Val;//thing1的x1和x2现在应该匹配
usleep(arc4random_uniform(50000));//睡眠0到50k微秒
_thing2.x2=thing2Val;//现在thing2的x1和x2也应该匹配
}
});
//在队列2上执行相同的操作
调度异步(队列2^
{
对于(int x=0;x<100;x++)
{
usleep(arc4random_uniform(50000));//睡眠0到50k微秒
int thing1Val=arc4random_均匀(10000);
int thing2Val=arc4random_均匀(10000);
_thing1.x1=thing1Val;
usleep(arc4random_uniform(50000));//睡眠0到50k微秒
_thing2.x1=thing2Val;
_thing1.x2=thing1Val;//thing1的x1和x2现在应该匹配
usleep(arc4random_uniform(50000));//睡眠0到50k微秒
_thing2.x2=thing2Val;//现在thing2的x1和x2也应该匹配
}
});
//每隔0.1秒记录thing1和thing2中的值
self.aTimer=[NSTimer scheduledTimerWithTimeInterval:.1
目标:自我
选择器:@selector(日志内容:)
用户信息:无
重复:是];
//5秒后,关闭计时器。
self.secondTimer=[NSTimer scheduledTimerWithTimeInterval:5.0
目标:自我
选择器:@selector(stopRepeatingTimer:)
用户信息:无
重复:否];
返回YES;
}
-(无效)停止重复计时器:(NSTimer*)计时器
{
[self.aTimer失效];
}
-(void)日志:(NSTimer*)计时器
{
NSString*相等字符串;
如果(_thing1.x1==_thing1.x2)
{
equalString=@“相等”;
}
其他的
{
equalString=@“不相等”;
}
NSLog(@“%@:thing1.x1=%d,thing1.x2=%d),
相等字符串,
_thing1.x1,
_thing1.x2);
如果(_thing2.x1==_thing2.x2)
{
equalString=@“相等”;
}
其他的
{
equalString=@“不相等”;
}
NSLog(@“%@:thing2.x1=%d,thing2.x2=%d),
相等字符串,
_thing2.x1,
_thing2.x2);
}
在上面的代码中,每个队列创建一系列随机值,并在重复循环中将一对对象的x1和x2属性设置为这些随机值。它会在设置每个对象的x1和x2属性之间延迟一个小的随机间隔。这种延迟模拟了一个后台任务,它需要花费一定的时间来完成应该是原子的工作。它还引入了一个窗口,在该窗口中,另一个线程可以在当前线程能够设置第二个值之前更改第二个值

如果您运行上面的代码,您几乎肯定会发现thing1和thing2的x1和x2值有时是不同的

原子属性对上面的代码没有帮助。您需要在设置每个对象的x1和x2属性之间声明某种锁(可能使用
@synchronized
指令)

(请注意,我在论坛编辑器中将上述代码拼凑在一起。我没有尝试编译它,更不用说调试它了。毫无疑问,这里有一些打字错误。)


(注2,对于编辑我的代码的人:代码格式是风格和个人品味的问题。我使用了“Allman缩进”的变体。)我欣赏打字错误更正,但我鄙视K&R风格的缩进。不要把你的风格强加在我的代码上。

属性是
原子的
意味着所有操作都由读取执行,所有
typedef struct {
    NSUInteger x;
    NSUInteger xSquared;  // cached value of x*x
} Data;


@interface Producer : NSObject

@property (nonatomic) Data latestData;
@property (nonatomic) NSObject *latestObject;

@end


@implementation Producer

- (void)startProducing
{
    // Produce new Data structs.
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (NSUInteger x = 0; x < NSUIntegerMax; x++) {
            Data newData;
            newData.x = x;
            newData.xSquared = x * x;

            // Since the Data struct is too large for a single store,
            // the setter actually updates the two fields separately.
            self.latestData = newData;
        }
    });

    // Produce new objects.
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (true) {
            // Release the previous value; retain the new value.
            self.latestObject = [NSObject new];
        }
    });

    [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(logStatus) userInfo:nil repeats:YES];
}

- (void)logStatus
{
    // Implicitly retain the current object for our own uses.
    NSObject *o = self.latestObject;
    NSLog(@"Latest object: %@", o);

    // Validate the consistency of the data.
    Data latest = self.latestData;
    NSAssert(latest.x * latest.x == latest.xSquared, @"WRONG: %lu^2 != %lu", latest.x, latest.xSquared);
    NSLog(@"Latest data: %lu^2 = %lu", latest.x, latest.xSquared);
}

@end



int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [[Producer new] startProducing];
        [[NSRunLoop mainRunLoop] run];
    }
    return 0;
}
// clang -arch i386 -framework Foundation atomic.m -o atomic ; ./atomic
#import <Foundation/Foundation.h>

@interface MyObject : NSObject {
    long long i;
}
@property (nonatomic) long long i;
@end

@implementation MyObject
@synthesize i;
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
        dispatch_queue_t queue3 = dispatch_queue_create("queue3", DISPATCH_QUEUE_CONCURRENT);

        MyObject *obj = [MyObject new];

        long long value1 = 0;
        long long value2 = LLONG_MAX;

        dispatch_async(queue2, ^{
            while (YES) {
                obj.i = value1;
            }
        });

        dispatch_async(queue3, ^{
            while (YES) {
                obj.i = value2;
            }
        });
        while (YES) {
            long long snapshot = obj.i;
            if (snapshot != value1 && snapshot != value2) {
                printf("***PANIC*** Got %lld (not %lld or %lld)\n", snapshot, value1, value2);
            }
        }
    }
    return 0;
}
***PANIC*** Got 4294967295 (not 0 or 9223372036854775807)
***PANIC*** Got 9223372032559808512 (not 0 or 9223372036854775807)
@interface MyObject : NSObject {
    id _something;
}
- (id)something;
- (void)setSomething:(id)newSomething;
@end

@implementation MyObject

- (id)something {
    return _something;
}

- (void)setSomething:(id)newSomething {
    [newSomething retain];
    [_something release];
    _something = newSomething;
}

@end
- (id)something {
    return [[_something retain] autorelease];
}