如果一个方法在同一时间被调用两次,如何只执行一次?

时间:2009-10-02 03:33:49

标签: iphone objective-c cocoa-touch uikit

我们在iPhone SDK中有一个委托方法。问题是,操作系统在同一时间调用此方法两次。这个方法做了一些繁重的工作,所以我不想执行两次逻辑。有什么好方法可以检测到这种情况并阻止其中一个运行?


忘记提及,它是从不同的线程调用的。

8 个答案:

答案 0 :(得分:10)

一种方法是您在输入方法时设置的BOOL成员,并在离开时清除。如果在输入时设置了变量,您就知道它已经在执行并且可以返回。

假设您正在从多个线程调用,您将需要锁定对此关键检查/设置区域的访问。 NSLock对此有好处。

下面的代码有两个实现:myMethod1使用NSLock,myMethod2使用@synchronize显示。

@interface MyClass : NSObject
{
    NSLock* theLock;
    BOOL isRunning;
}
@end

@implementation MyClass

-(id)init
{
    self = [super init];
    if(self != nil)
    {
        theLock = [[NSLock alloc] init];
        isRunning = NO;
    }
    return self;
}

-(void)dealloc
{
    [theLock release];
    [super dealloc];
}

// Use NSLock to guard the critical areas
-(void)myMethod1
{
    [theLock lock];

    if(isRunning == YES)
    {
        [theLock unlock]; // Unlock before returning
        return;
    }

    isRunning = YES;        

    // Do fun stuff here

    isRunning = NO;

    [theLock unlock];    
}

// This method uses @synchronize
-(void)myMethod2
{
    @synchronized(self)
    {
        if(isRunning == YES)
        {
            return;
        }

        isRunning = YES;

        // Do stuff here.

        isRunning = NO;
    }
}
@end

答案 1 :(得分:5)

哇。答案是正确的,但过度设计。只需使用@synchronized()

foo.h中:

@interface Foo
{
    id expensiveResult;
}
- (void) bar;
@end

Foo.m:

@implementation Foo
- (void) bar
{
    @synchronized(self) {
        if (expensiveResult) return expensiveResult;
        .... do expensive stuff here ....
        expensiveResult = [theResult retain];
    }
    return expensiveResult;
}
@end

如果您有多个Foo实例并希望保证所有实例的排他性,请在+(void)initialize中创建一个全局变量 - 一个NSString就可以了 - 并且@synchronized()就可以了。

然而,你的问题提出了一个更重要的问题。特别是,除非您非常明确地将应用程序配置为导致确实发生这种情况,否则永远不会同时调用同一方法两次。

答案提供的声音更像是对症状的修复,而不是解决实际问题。

注意:这依赖于expensiveResult为nil,因为所有iVars在实例化时都是零。显然,如果要重新计算,请将ivar重置为nil。

答案 2 :(得分:2)

最简单的是设置一个标志。

- (void)action {
  if (flag_is_set == NO) {
    flag_is_set = YES;
    do stuff
    flag_is_set = NO;
  }
}

这不是100%安全,但因为您可能会遇到一些罕见的互锁情况。

如果你可以在线程上处理一些睡眠,请使用nslock

- (id)init {
  theLock = [[NSLock alloc] init];
}
- (void)action {
  [theLock lock];
  doStuff
  [theLock unlock];
}

当线程2进入锁定调用并且线程1已经拥有它时,它将处于执行循环中直到锁定被释放,然后它将再次启动。如果您在此主题上有UI,那么您的应用程序将显示为冻结

答案 3 :(得分:2)

对于多个“生产者”线程同时调用相同函数的问题,一些给定的答案是可接受的解决方案但是你可能最好弄清楚为什么多个线程同时调用同一个代码块的原因。可能是您将此委托分配给多个事件处理程序或类似的东西。您可能已经注意到这种情况正在发生,因为某些共享状态正在被破坏,或者函数的输出对于函数末尾的“全局”状态是不正确的。将一个bandaid置于事实上2个线程处于给定函数中(当明确表示线程在编写时并不是主要关注点)并不一定会给你正确的结果。线程并不是一件容易的事情,共享状态使得做正确变得非常棘手,确保在尝试修补它之前完全理解为什么会发生这种情况。

话虽这么说,如果你采取了bandaid方法,最好用一个锁来做一个锁,其中每个线程最终都会执行该函数而不是让它们挽救,如果该函数已经全部启动,因为客户端代码它看起来像那么漫长而且“繁重”的过程已经完成,他们可能会检查结果。

答案 4 :(得分:0)

如果这些调用是同步的,那么一次只有一个调用,你可以在你的类上有一个变量,叫做“initialized”,它从false开始,在初始化时设置:

if (!initialized) {
    // handle delegated call
    initialized = 1;
}

如果从多个线程调用委托,则需要将所有这些都放在互斥块中。

答案 5 :(得分:0)

以下是使用objective-c锁作为互斥锁的方法。

http://rosettacode.org/wiki/Mutex#Objective-C

存在互斥锁以允许对特定代码块的互斥访问。在该方法中,您可以执行以下操作:

[methodLock lock]; //Blocks and waits until the lock is released
//...do stuff
[methodLock unlock]; //Releases the lock once the code has executed.

这将确保在// do stuff代码块中只允许一个线程。

编辑:我再次阅读了这个问题;在锁内我会检查标志是否运行(使用BOOL)

答案 6 :(得分:0)

使用pthread_once() - 它明确地设计为完成此操作。唯一的问题是你传递的函数不能接受任何参数,所以你必须使用全局变量来传递信息。

答案 7 :(得分:-1)

如果在同一时间调用它们,我猜它们是在线程中调用的吗?

您可以做的是定义BOOL @property(例如,名为isRunning),其属性为atomic(默认设置)。这样,可以从不同的线程安全地访问此属性,然后您可以执行以下操作:

if (isRunning)
    return ;
isRunning = YES;
// ...
// Your code here
// ...
usRunning = NO;

您也可以确保自己做得对。如果你的方法被调用两次,也许你做错了(或者可能是正常的,我不知道你在做什么;)