首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >种族状况如何?委托和多线程

种族状况如何?委托和多线程
EN

Stack Overflow用户
提问于 2010-07-09 22:14:12
回答 2查看 1.4K关注 0票数 3

我很困惑多线程是如何与代表一起工作的。

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

当对象"B“想通知委托时,它会通知:

代码语言:javascript
运行
复制
[[self delegate] performSelectorOnMainThread:@selector(didFinish:) withObject:self waitUntilDone:[NSThread isMainThread]];

“委托”属性是一个赋值的原子@属性。因此,根据目标c手册的说法,生成的getter将执行[委托保留自动释放]。

"A“的脱敏方法是:

代码语言:javascript
运行
复制
- (void)dealloc
{
    [b setDelegate:nil];
    [b release];
    [super dealloc];
}

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

  1. 主线程:调用脱脂剂
  2. 其他线程:B调用保留
  3. 主线程:调用b setDelegate:0
  4. 其他线程:调用performSelectorOnMainThread

在第二步,保留似乎不可能成功,因为dealloc已经承诺--这是比赛条件吗?如果您对正在被释放的对象调用retain,会发生什么情况?它真的会发生吗?

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

(这源于我之前问过的一个稍微类似但更简单的问题/答案,如何使用多个线程处理setDelegate。)

更新

正如公认的答案所证明的那样,这是一种种族行为。

解决我原来的问题的方法是避免这种情况,我已经更新了如何处理setDelegate:当使用多线程时以说明这一点。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-07-10 10:12:56

我不认为deallocretain/release之间存在锁。下面的示例中有一个dealloc方法,其中包含一个sleep() (是否有人知道sleep()是否中断了锁?我不认为是这样,但你永远不会知道)。一个更好的例子可能是反复实例化/销毁A和B的实例,直到出现这里提到的情况,而不使用sleep()

在我的例子中,视图控制器可以是任何东西:

代码语言:javascript
运行
复制
-(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];
}

答:

代码语言:javascript
运行
复制
#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

B (.h):

代码语言:javascript
运行
复制
#import "A.h"

@interface B : NSObject {
    A *delegate;
}

-(void) retainDelegate;

@property (nonatomic, assign) A *delegate;

@end

B (.m):

代码语言:javascript
运行
复制
#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的EXC_BAD_ACCESS releaseDelegate方法中崩溃。以下是NSLogs的输出:

代码语言:javascript
运行
复制
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,保留计数就不再重要了。该对象将被销毁(这可能是显而易见的,但我想知道,如果您检查了self的retainCount,如果对象保留了,则没有调用super,会发生什么情况?(疯狂的想法)。现在,如果我们将A的-dealloc修改为首先将B的委托设置为nil,则程序可以工作,但这只是因为我们在releaseDelegate中的B中没有delegate

我不知道这是否真的回答了您的问题,但是假设睡眠()不是以某种方式破坏线程锁,那么在dealloc被调用时,在retain之前应该会发生完全相同的行为。

票数 2
EN

Stack Overflow用户

发布于 2010-07-10 00:41:25

这与我对堆栈溢出的猜测差不多,但我认为-dealloc-retain-release同步到相同的锁上,如果不是原子的,那就太疯狂了。这个锁并不是在dealloc中神奇地获得的,因为很明显,这个锁是由您自己的代码填充的,而是在发布时,它在执行其dealloc操作时只持有相同的锁。(这可能是你不应该直接打电话给dealloc的原因之一)

现在,在对象B中,self委托调用对象A的respect,如果我说得对,这是关于dealloc和retain的原子化,并且要么发生在-A dealloc之前,因为它将发生在-A发布之前,或者发生在-A dealloc之后,这取决于它的时间。

在第一种情况下,-A保留发生在-A发布之前,其结果是显而易见的:对象A将在从同一个访问器获得以下-A自动释放之前不会被释放,而对象B将在保持不变的对象A上调用委托方法。

第二种情况要复杂得多,从这一点开始,我们将离开事实的坚实基础,一起穿越记忆的黑暗沼泽,进入一堆最疯狂的猜测。我认为,在第二种情况下,-A dealloc试图将对象B的委托(正如前面所说的,而另一个线程正在等待获取其委托的锁)设置为零。但是,有了原子属性,A就必须在等待用于保留/释放/去分配的锁A时获得已获得和正在使用的锁B,这显然是在使用中。

因此,我认为这将导致僵局,尽管我再次完全不确定,这一答案主要是基于对什么是锁定和何时锁定的猜测。同样,我唯一可行的解决方案(但不要停止寻找,必须有更好的方法)是保留委托,至少在第二个线程运行时是这样,并防止它首先被释放。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3216888

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档