【问题标题】:How to get/set a global variable (BOOL) from within a background-thread? (Xcode)如何从后台线程中获取/设置全局变量(BOOL)? (Xcode)
【发布时间】:2011-02-10 17:43:21
【问题描述】:

从我的主线程中,我启动了一个图像加载器方法 method-A(如下)。问题是,如果在进行新的 method-A 调用时 method-A 未完成,则图像加载将从头开始。

我想要做的是,取消任何 new method-A 调用,而 previous method-A 调用仍在工作......我(尝试)现在这样做的方式是使用一个简单的全局 BOOL 变量 (BOOL imageLoaderBusy) 并使用它来跟踪 method-A 是否还在工作(如下图)。

问题是,该变量有时似乎被忽略了,并且新的 method-A 调用不受欢迎地开始了......我不知道。也许您需要一种特殊的方式来创建全局变量以使它们在多个线程中可访问/有效?

谁能告诉我我做错了什么?谢谢。

//Method-A called like this:

[self performSelectorInBackground:@selector(loadPagesWithGraphics:) withObject:nil];


//Method-A

-(IBAction)loadPagesWithGraphics:(id)sender{

    NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];

    if(!imageLoaderBusy){

    imageLoaderBusy = YES;

        // Load Images

        }

       imageLoaderBusy = NO;

       [arPool release];
}

提前致谢。

【问题讨论】:

  • 请注意,您的变量不应是全局变量。它应该是一个实例变量,因为您的方法是一个实例方法,并且图像加载可能是指由实例存储/引用的图像。
  • @Bavarious 感谢您提供此信息,因为我仍然对变量和多线程的确切机制缺乏清晰的了解。

标签: iphone objective-c multithreading xcode ios


【解决方案1】:

无论变量是实例变量还是全局变量,如果多个线程可能同时写入该变量,则需要锁定该段代码。例如,

-(IBAction)loadPagesWithGraphics:(id)sender{
    @synchronized(self) {
        if (imageLoaderBusy) return;
        imageLoaderBusy = YES;
    }

    NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
    // Load Images
    imageLoaderBusy = NO;
    [arPool release];
}

假设该方法的两次执行同​​时发生在线程 A 和 B 中,并且 A 先获得锁,因此线程 B 等待锁被释放。从 A 的角度来看,imageLoaderBusy == NO 所以它不会返回,设置imageLoaderBusy = YES,然后释放锁。

由于锁已被释放,线程 B 可以开始执行。它检查imageLoaderBusy,并且由于线程A已将其设置为YES,因此该方法立即在线程B中返回。

线程 A 继续加载图像并将 imageLoaderBusy 设置为 NO

请注意,这意味着如果在某个线程中再次调用该方法,它将被执行并再次加载图像。我不确定这是否是您的预期行为;如果不是,则需要再次检查以确定图像是否已加载。例如,

-(IBAction)loadPagesWithGraphics:(id)sender{
    if (imagesHaveBeenLoaded) return;

    @synchronized(self) {
        if (imageLoaderBusy) return;
        imageLoaderBusy = YES;
    }

    NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
    // Load Images
    [arPool release];

    imageLoaderBusy = NO; // not strictly necessary
    imagesHaveBeenLoaded = YES;
}

您不需要将所有方法都放在 @synchronize 块中。事实上,关键部分通常应该保持较小,尤其是当锁应用于整个对象时 (self)。如果整个方法是一个临界区,线程 B 必须等到所有图像都加载完毕后,才会注意到另一个线程已经很忙/已经加载了图像。

【讨论】:

    【解决方案2】:

    尝试改变这种方式:

    -(IBAction)loadPagesWithGraphics:(id)sender{
        if( imagesDidLoad )  return;
    
        @synchronized(self) {
    
           NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
    
            // Load Images
    
           [arPool release];
    
           //set global ivar
           imagesDidLoad = YES;
       }
    }
    

    在方法-A中

    添加

    -(void) methodA {
        if( !imagesDidLoad )
           [self performSelectorInBackground:@selector(loadPagesWithGraphics:) withObject:nil];
    }
    

    【讨论】:

    • 您好,马克斯,感谢您的快速回答。您能否详细说明这个 @synchronized(self) 对我的方法有什么作用?
    • 基本上@synchronized(self) 阻塞所有其他线程的方法
    • 非常感谢。这帮助很大。
    【解决方案3】:

    Method-a 中,在你的主线程上调用一个setter 来设置BOOL

    方法是:- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

    【讨论】:

    • 感谢托马斯的快速回答。这涵盖了设置。那么变量的获取呢?还是可以按原样完成?
    • 我想这是可能的。您必须小心死锁(多个线程访问同一个 ivar),这可以通过 NSLock 解决。我对您的应用了解不多,但是如果您想从线程设置 ivars 并获取它们,则会遇到很多问题。也许重新检查你的设计?如果这是不可避免的,尽量不要经常这样做,因为它破坏了多线程的目的
    猜你喜欢
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多