Objective-C线程安全计数器 [英] Objective-C Thread Safe Counter
问题描述
我正在尝试以线程安全的方式来控制网络活动指示器。
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
不大于零,因此 setNetworkActivityIndicatorVisible
方法将以 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
是显然大于零,并且 setNetworkActivityIndicatorVisible
方法将使用 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 $ c中$ c>块或类似内容。
The bottom line is you're going to need to wrap the whole thing in a @synchronize
block or something similar.
这篇关于Objective-C线程安全计数器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!