Objective-C线程安全计数器 [英] Objective-C Thread Safe Counter

查看:75
本文介绍了Objective-C线程安全计数器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试以线程安全的方式来控制网络活动指示器。

I am trying to keep the network activity indicator under control in a thread-safe way.

这是我目前的操作方式,但我认为必须更好地做到这一点。我一直在寻找使用锁的方法,但这似乎是一项昂贵的操作。我一直在查看OSAtomicAdd,但是无法弄清楚在这种情况下如何使用它。

Here is the way I am currently doing, but I think there must be a better way to do it. I was looking of using locks but it seems like an expensive operation. I have been looking at OSAtomicAdd but cant figure out exactly how to use it in this scenario.

+ (void)start
{
    [self counterChange:1];
}

+ (void)stop
{
    [self counterChange:-1];
}

+ (void)counterChange:(NSUInteger)change
{
    static NSUInteger counter = 0;
    static dispatch_queue_t queue;
    if (!queue) {
        queue = dispatch_queue_create("NetworkActivityIndicator Queue", NULL);
    }
    dispatch_sync(queue, ^{
        if (counter + change <= 0) {
            counter = 0;
            [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        } else {
            counter += change;
            [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        }
    });
}

如何使用OSAtomicAdd完成此类操作?

How can something like this be done using OSAtomicAdd?

推荐答案

您不能仅依靠 OSAtomicAdd 之类的东西来同步这种操作。需要锁定整个操作以确保其成功运行。

You can't rely on something like OSAtomicAdd alone to synchronise this kind of operation. The whole operation needs to be locked to make sure it works successfully.

请考虑这个答案,基本上可以归结为:

Consider the solution suggested in this answer, which basically comes down to this:

static volatile int32_t NumberOfCallsToSetVisible = 0;
int32_t newValue = OSAtomicAdd32((setVisible ? +1 : -1), &NumberOfCallsToSetVisible);
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:(newValue > 0)];

如果此代码是从一个线程中调用的,则 setVisible 设置为 YES ,对 OSAtomicAdd32 的调用将为 NumberOfCallsToSetVisible加1 导致 newValue 设置为1。

If this code is called from one thread, with setVisible set to YES, the call to OSAtomicAdd32 is going to add 1 to NumberOfCallsToSetVisible resulting in newValue being set to 1.

现在考虑如果该线程发生了什么在下一行执行之前被抢占,另一个线程调用 setVisible 设置为 NO 的函数。这次对 OSAtomicAdd32 的调用将从 NumberOfCallsToSetVisible 减去1,从而产生 newValue 设置为0。

Now consider what happens if that thread is preempted before the next line is executed, and another thread calls the function with setVisible set to NO. This time the call to OSAtomicAdd32 is going to substract 1 from NumberOfCallsToSetVisible resulting in newValue being set to 0.

如果第二个线程继续执行,并执行了下一行,则 newValue 不大于零,因此 setNetworkActivityIndi​​catorVisible 方法将以 NO 调用。此时活动指示符还是看不见的,因此它什么也没做,但是也没有任何危害。

If this second thread continues, and the next line is executed, newValue is not greater than zero, so the setNetworkActivityIndicatorVisible method will be called with NO. At this point the activity indicator wasn't visible anyway, so this does nothing, but it doesn't do any harm either.

但是,最终,我们将切换回 newValue 设置为1的第一个线程。因此,当该线程执行下一行时, newValue 是显然大于零,并且 setNetworkActivityIndi​​catorVisible 方法将使用 YES 调用,从而使活动指示器可见。

However, eventually we're going to switch back to the first thread where newValue is set to 1. So when that thread executes the next line, newValue is obviously greater than zero, and the setNetworkActivityIndicatorVisible method will be called with YES, making the activity indicator visible.

因此我们调用了一次函数,将 setVisible 设置为 YES ,并再次将 setVisible 设置为 NO 。您可能希望这会导致活动指示器不可见,但事实并非如此。实际上,如果没有其他呼叫,它将永远保持可见状态。

So we've called the function once with setVisible set to YES and once more with setVisible set to NO. You would expect this would result in the activity indicator being invisible, yet that's not what has happened. In fact if no other calls are made, it's going to remain visible forever. This is clearly not right.

最重要的是,您需要将整个内容包装在 @synchronize 块或类似内容。

The bottom line is you're going to need to wrap the whole thing in a @synchronize block or something similar.

这篇关于Objective-C线程安全计数器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆