2

スレッドセーフな方法でネットワーク アクティビティ インジケーターを制御しようとしています。

これが私が現在行っている方法ですが、もっと良い方法があるはずだと思います。ロックの使用を探していましたが、高価な操作のようです。私は OSAtomicAdd を見てきましたが、このシナリオでそれを使用する方法を正確に理解することはできません.

+ (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 を使用して、このようなことを行うにはどうすればよいでしょうか?

4

5 に答える 5

1

の代わりに、同じファミリの関数OSAtomicAdd32を使用することをお勧めします。OSAtomicCompareAndSwap32

+ (void)counterChange:(NSUInteger)change
{
  static int32_t counter = 0;
  int32_t localCounter, newCounter;
  do
  {
    localCounter = counter;
    newCounter = localCounter + change;
    newCounter = newCounter <= 0 ? 0 : newCounter;
  } while (!OSAtomicCompareAndSwap32(localCounter, newCounter, &counter));
  [UIApplication sharedApplication].networkActivityIndicatorVisible = counter > 0;
}

localCounterこの関数はを の現在の値と比較し、counterそれらが一致する場合にのみ に変更counternewCounter、そのすべてをアトミックに変更します。counter現在のスレッドが取得localCounterしてから を呼び出すまでの間に他のスレッドが変更された場合OSAtomicCompareAndSwap32、チェックは失敗し、再試行されます。

一部のスレッドが永遠にループする可能性があるように見えたとしても、この構造は実際の状況では十分安全です。

于 2013-06-18T20:19:29.757 に答える
0

問題を複雑にし、必要な数を UIApplication に伝える際にスレッド同期を修正する必要があるという事実を解決しない場合に、これらすべてのアトミック操作が使用されている理由はわかりません。

@synchronized を使用するという提案は、UIApplication のインクリメントと呼び出しに関するミューテックスを提供するため、適切な解決策です。@synchronized をベンチマークすると、驚くほど高速であることがわかります。この種のことは非常にまれであるため、アトミック変数と比較とスワップはエラーが発生しやすく、不要です。これを行わない唯一の理由は、(self) が他の多くの部分で同期されている場合です。この場合、この目的のために NSObject を保持するか、NSLock と同等のものを使用できます。

したがって:

+ (void) incrementActivityCounter {
    [self changeActivityCounter:1];
}

+ (void) decrementActivityCounter {
    [self changeActivityCounter:-1];
}

+ (void) changeActivityCounter:(int)change {
    static int counter = 0;
    @synchronized(self) {
      counter += change;
      [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:counter > 0];
    }
}
于 2015-01-21T00:28:02.147 に答える
0

NSLock (iOS 2.0 以降および OS X 10.0 以降で利用可能) は、あなたが探しているものです。

NSLock オブジェクトは、同じアプリケーション内で実行される複数のスレッドの操作を調整するために使用されます。NSLock オブジェクトを使用して、アプリケーションのグローバル データへのアクセスを仲介したり、コードのクリティカル セクションを保護して、アトミックに実行できるようにすることができます。

アプリのデリゲートと呼び出しでロックを初期化し-lock-unlockカウンター コードを囲むことができます。

// Assuming the application delegate implements -counterChangeLock
// to return a momoized instance of NSLock

+ (NSLock *)counterChangeLock
{
    return [(AppDelegate *)([UIApplication sharedApplication].delegate) counterChangeLock];
}

+ (void)start
{
    [[self counterChangeLock] lock]; // blocks if counter is locked already

    // safely increment counter

    [[self counterChangeLock] unlock];
}

+ (void)stop
{

    [[self counterChangeLock] lock];

    // safely decrement counter

    [[self counterChangeLock] unlock];
}
于 2013-10-08T18:07:28.793 に答える