Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 使用无锁读取实现线程安全的可失效缓存?_Objective C_Thread Safety_Lock Free_Memory Barriers - Fatal编程技术网

Objective c 使用无锁读取实现线程安全的可失效缓存?

Objective c 使用无锁读取实现线程安全的可失效缓存?,objective-c,thread-safety,lock-free,memory-barriers,Objective C,Thread Safety,Lock Free,Memory Barriers,我试图在头脑中思考如何使用大致如下的API实现引用计数值的线程安全缓存机制:(注意:我使用的是Objective-C语法,但问题不是特定于语言的) 当有人请求-value时,如果它有一个现有的缓存值,它应该返回该值的-retain/-autoreleased版本。如果它没有值,或者该值无效,它应该使用在init time传入的生成块生成一个值,然后它应该缓存该值以备将来读取,直到有人调用-invalidate 假设我们不关心生成器块是否被多次调用(即,当第一个读卡器在生成器块中时,第二个读卡器到

我试图在头脑中思考如何使用大致如下的API实现引用计数值的线程安全缓存机制:(注意:我使用的是Objective-C语法,但问题不是特定于语言的)

当有人请求
-value
时,如果它有一个现有的缓存值,它应该返回该值的
-retain/-autoreleased
版本。如果它没有值,或者该值无效,它应该使用在init time传入的生成块生成一个值,然后它应该缓存该值以备将来读取,直到有人调用
-invalidate

假设我们不关心生成器块是否被多次调用(即,当第一个读卡器在生成器块中时,第二个读卡器到达),只要它返回的对象没有泄漏。第一次通过的非无等待实现可能类似于:

- (id)value
{
    id retVal = nil;
    @synchronized(self)
    {
        retVal = [mValue retain];
    }
    if (!retVal)
    {
        retVal = [[mGenerator() retain] retain]; // Once for the ivar and once for the return value
        id oldVal = nil;
        @synchronized(self)
        {
            oldVal = mValue;
            mValue = retVal;
        }
        [oldVal release];
    }
    return [retVal autorelease];
}

- (void)invalidate
{
    id val = nil;
    @synchronized(self)
    {
        val = mValue;
        mValue = nil;
    }
    [val release];
}
当然,这会导致糟糕的读取性能,因为并发读取是由锁序列化的。读写器锁可以改善这一点,但在读取路径上仍然非常缓慢。这里的性能目标是使缓存读取尽可能快(希望无锁)。如果我们必须计算一个新的值,那么读取速度慢是可以的,
-invalidate
速度慢也是可以的

所以。。。我正试图找出一种方法,使读取锁定/等待自由。我的第一个想法(有缺陷-见下文)是添加一个无效计数器,其值是原子的、单调递增的,并使用内存屏障读取。看起来是这样的:

- (id)value
{
    // I think we don't need a memory barrier before this first read, because
    // a stale read of the count can only cause us to generate a value unnecessarily,
    // but should never cause us to return a stale value.
    const int64_t startCount = mWriteCount;

    id retVal = [mValue retain];

    OSMemoryBarrier(); // But we definitely want a "fresh" read here.
    const int64_t endCount = mWriteCount;

    if (retVal && startCount == endCount)
    {
        return [retVal autorelease];
    }

    // Now we're in the slow path
    retVal = [mGenerator() retain]; // we assume generator has given us an autoreleased object

    @synchronized(self)
    {
        mValue = retVal;
        OSAtomicIncrement64Barrier(&mWriteCount);
    }
    return retVal;
}

- (void)invalidate
{
    id value = nil;
    @synchronized(self)
    {
        value = mValue;
        mValue = nil;
        OSAtomicIncrement64Barrier(&mWriteCount);
    }
    [value release];
}
但我已经看到了这里的问题。例如,读取路径中的
[mValue retain]
:我们需要保留该值,但在读取
mValue
和调用
-retain
之间的时间内,另一个线程可能会
-invalidate
,导致在进行retain调用时该值已被释放。因此,这种方法无法按原样工作。可能还有其他问题


有没有人已经解决了类似的问题并愿意分享?或者有一个指向野外类似事物的指针?

我最终采用了这种方法来解决这个问题:。它似乎工作得很好——读取性能比基于锁的方法快50倍(但这并不奇怪)。

我最终采用了这种方法解决问题:。它似乎工作得很好——读取性能比基于锁的方法快50倍(但这并不奇怪)

- (id)value
{
    // I think we don't need a memory barrier before this first read, because
    // a stale read of the count can only cause us to generate a value unnecessarily,
    // but should never cause us to return a stale value.
    const int64_t startCount = mWriteCount;

    id retVal = [mValue retain];

    OSMemoryBarrier(); // But we definitely want a "fresh" read here.
    const int64_t endCount = mWriteCount;

    if (retVal && startCount == endCount)
    {
        return [retVal autorelease];
    }

    // Now we're in the slow path
    retVal = [mGenerator() retain]; // we assume generator has given us an autoreleased object

    @synchronized(self)
    {
        mValue = retVal;
        OSAtomicIncrement64Barrier(&mWriteCount);
    }
    return retVal;
}

- (void)invalidate
{
    id value = nil;
    @synchronized(self)
    {
        value = mValue;
        mValue = nil;
        OSAtomicIncrement64Barrier(&mWriteCount);
    }
    [value release];
}