Ios Objective-C中原子/非原子的证据
阅读后,我试图在Objective-C中证明属性的原子性或非原子性。为此,我创建了一个名为Person的类 人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
@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];
}