iphone - 竞争条件与否?委托(delegate)和多线程

标签 iphone objective-c multithreading delegates

我对多线程如何与委托(delegate)一起工作感到困惑。

主线程有一个对象“A”,它创建了一个对象“B”。对象“A”是对象“B”的委托(delegate)。对象“B”使用线程来运行代码。

当对象“B”想要通知委托(delegate)时,它会:

[[self delegate] performSelectorOnMainThread:@selector(didFinish:) withObject:self waitUntilDone:[NSThread isMainThread]];

“委托(delegate)”属性是一个分配的、原子的@property。因此,根据 objective c manual,生成的 getter 将执行 [[delegate retain] autorelease]。 .

“A”的 dealloc 方法是:

- (void)dealloc
{
    [b setDelegate:nil];
    [b release];
    [super dealloc];
}

这似乎会导致线程像这样运行的可能情况:

  1. 主线程:调用 [A dealloc](由于调用 [a release])
  2. 其他线程:b调用[A retain](由于调用[self delegate])
  3. 主线程:调用 [b setDelegate:nil]
  4. 其他线程:调用performSelectorOnMainThread

在第 2 步,似乎 retain 无法成功,因为 dealloc 已经提交 - 这是竞争条件吗?如果你在一个正在被释放的对象上调用 retain 会发生什么?它真的会发生吗?

如果是竞争条件,带委托(delegate)的多线程对象通常如何避免?

(这源于我之前提出的一个稍微相似但更简单的问题/答案,how to handle setDelegate with multiple threads。)

更新

正如已接受的答案所证明的那样,这是一个竞争条件。

我原来问题的解决方案是一起避免这种情况,我已经更新了 How to handle setDelegate: when using multipe threads来展示这一点。

最佳答案

我认为 deallocretain/release 没有锁。下面的例子有一个 dealloc 方法,其中有一个 sleep() (有人知道 sleep() 是否会破坏锁吗?我不知道认为它确实如此,但你永远不会知道)。一个更好的例子可能是重复实例化/销毁 A 和 B 的实例,直到出现此处提到的情况,没有 sleep()

View Controller ,在我的例子中,但可以是任何东西:

-(void)septhreadRetainDel
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSLog(@"[thread#2] sleep(1.f);");
    sleep(1.f);
    NSLog(@"[thread#2] [b retainDelegate];");
    [b retainDelegate];
    NSLog(@"[thread#2] sleep(2.f);");
    sleep(2.f);
    NSLog(@"[thread#2] [b release];");
    [b release];
    [pool release];
}

- (void)viewDidLoad {
    NSLog(@"-(void)viewDidLoad:");
    [super viewDidLoad];
    NSLog(@"a = [[A alloc] init];");
    a = [[A alloc] init];
    NSLog(@"[a autorelease];");
    [a autorelease];
    NSLog(@"b = [[B alloc] init];");
    b = [[B alloc] init];
    NSLog(@"b.delegate = a;");
    b.delegate = a;
    NSLog(@"[NSThread detachNewThreadSelector:@selector(septhreadRetainDel) toTarget:self withObject:nil];");
    [NSThread detachNewThreadSelector:@selector(septhreadRetainDel) toTarget:self withObject:nil];
}

答:

#import "A.h"

@implementation A

-(void)dealloc
{
    NSLog(@"A: dealloc; zzz for 2s");
    sleep(2.f);
    NSLog(@"A: dealloc; waking up in time for my demise!");
    [super dealloc];
}
-(id)retain
{
    NSLog(@"A retain (%d++>%d)", self.retainCount, self.retainCount+1);
    return [super retain];
}
-(void)release
{
    NSLog(@"A release (%d-->%d)", self.retainCount, self.retainCount-1);
    [super release];
}

@end

乙(.h):

#import "A.h"

@interface B : NSObject {
    A *delegate;
}

-(void) retainDelegate;

@property (nonatomic, assign) A *delegate;

@end

B (.m):

#import "B.h"

@implementation B

@synthesize delegate;

-(void)retainDelegate
{
    NSLog(@"B:: -(void)retainDelegate (delegate currently has %d retain count):", delegate.retainCount);
    NSLog(@"B:: [delegate retain];");
    [delegate retain];
}
-(void)releaseDelegate
{
    NSLog(@"B releases delegate");
    [delegate release];
    delegate = nil;
}

-(void)dealloc
{
    NSLog(@"B dealloc; closing shop");
    [self releaseDelegate];
    [super dealloc];
}

-(id)retain
{
    NSLog(@"B retain (%d++>%d)", self.retainCount, self.retainCount+1);
    return [super retain];
}
-(void)release
{
    NSLog(@"B release (%d-->%d)", self.retainCount, self.retainCount-1);
    [super release];    
}

@end

程序最终在 B 的 releaseDelegate 方法处崩溃并返回 EXC_BAD_ACCESS。以下是 NSLogs 的输出:

2010-07-10 11:49:27.044 race[832:207] -(void)viewDidLoad:
2010-07-10 11:49:27.050 race[832:207] a = [[A alloc] init];
2010-07-10 11:49:27.053 race[832:207] [a autorelease];
2010-07-10 11:49:27.056 race[832:207] b = [[B alloc] init];
2010-07-10 11:49:27.058 race[832:207] b.delegate = a;
2010-07-10 11:49:27.061 race[832:207] [NSThread detachNewThreadSelector:@selector(septhreadRetainDel) toTarget:self withObject:nil];
2010-07-10 11:49:27.064 race[832:4703] [thread#2] sleep(1.f);
2010-07-10 11:49:27.082 race[832:207] A release (1-->0)
2010-07-10 11:49:27.089 race[832:207] A: dealloc; zzz for 2s
2010-07-10 11:49:28.066 race[832:4703] [thread#2] [b retainDelegate];
2010-07-10 11:49:28.072 race[832:4703] B:: -(void)retainDelegate (delegate currently has 1 retain count):
2010-07-10 11:49:28.076 race[832:4703] B:: [delegate retain];
2010-07-10 11:49:28.079 race[832:4703] A retain (1++>2)
2010-07-10 11:49:28.081 race[832:4703] [thread#2] sleep(2.f);
2010-07-10 11:49:29.092 race[832:207] A: dealloc; waking up in time for my demise!
2010-07-10 11:49:30.084 race[832:4703] [thread#2] [b release];
2010-07-10 11:49:30.089 race[832:4703] B release (1-->0)
2010-07-10 11:49:30.094 race[832:4703] B dealloc; closing shop
2010-07-10 11:49:30.097 race[832:4703] B releases delegate
Program received signal:  “EXC_BAD_ACCESS”.

一旦 -dealloc 被调用,保留计数就不再重要。该对象将被销毁(这可能是显而易见的,但我想知道如果您检查了自己的 retainCount 并且如果对象保留了不调用 [super dealloc] 会发生什么……疯狂的想法)。现在,如果我们修改 A 的 -dealloc 以首先将 B 的委托(delegate)设置为 nil,则程序可以运行,但这只是因为我们没有设置 delegatereleaseDelegate 中的 B 中。

我不知道这是否真的回答了你的问题,但假设 sleep() 不会以某种方式破坏线程锁,当 dealloc保留

关于iphone - 竞争条件与否?委托(delegate)和多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3216888/

相关文章:

iphone - iOS 设备和蓝牙

iphone - iOS - 使用关联时出现 "This class is not key value coding-compliant"?

ios - 仅按标准距离垂直/水平滑动

iphone - 如何通过 iPhone 应用程序录制音频?

ios - 如何在ios中生成UUID

iphone - objective-c - 横向 UIView 自动调整大小

objective-c - 如何在 Interface Builder 中更改 UIButton 的背景图像并保留其形状?

c# - 如何为线程池中的每个新线程初始化一个标有 ThreadStaticAttribute 的静态字段?

java - Netty 分块输入流

c++ - Qt多线程: How to append few QRunnable tasks to QThreadPool