线程安全的NSMutableDictionary

时间:2016-09-28 07:11:53

标签: objective-c multithreading nsdictionary

我写的类SafeMutableDictionary继承了NSMutableDictionary 类仅实现“原始”方法,must be inherits 来自NSDictionary:

sudo u+x gradlew.sh

来自NSMutableDictionary:

- (instancetype)init;
- (instancetype)initWithObjects:(const id [])objects forKeys:(const id<NSCopying>[])keys count:(NSUInteger)cnt;
- (NSUInteger)count;
- (id)objectForKey:(id)key;
- (NSEnumerator*)keyEnumerator;

线程安全支持使用NSMutableDictionary类型的内部变量,它包含所有数据

- (void)removeObjectForKey:(id)key;
- (void)setObject:(id)obj forKey:(id)key;

并且每次访问使用@interface SafeMutableDictionary () { __strong NSMutableDictionary* _dictEmbedded; } 阻止

@synchronized

使用github中的完整代码回购。

但是,不幸的是,我仍然遇到像

这样的错误
- (id)objectForKey:(id)key{
    @synchronized (_dictEmbedded) {
        return [_dictEmbedded objectForKey:key];
    }
}

所以,我有一些问题:
1)我的实施是否正确?我错过了什么? 2)是否存在更为着名且经过测试的解决方案?
3)同时从main和bg线程访问容器的最佳实践是什么? 可能是最糟糕的做法是做这样的继承,并且更好地使用原始容器+关注线程安全

3 个答案:

答案 0 :(得分:2)

答案 1 :(得分:1)

这里有两个不同的要求:

  1. 多线程安全,即当多个线程同时访问时,集合(或您的字典中的字典)保持一致。

  2. 当同时修改集合时,稳健性,即迭代器(有时称为枚举器)保持一致(例如,枚举集合并删除枚举器返回的所选元素)。即使在单线程环境中,这也是一个挑战。

  3. 您的解决方案解决了要求1.但您收到的错误消息是关于要求2. NSDictionary实例不健全。很少有收集要求(使用任何编程语言)。

答案 2 :(得分:0)

@synchronized向您保证,您无法同时从多个主题访问某个对象,但如果出现这种情况,并不能保证一切正常。

我为解决这个问题所做的是创建一个基类NBSharedObjectWithLock: 有了这个课程,你可以:

myObjetc = [[MyClassInheritingFromNBSharedObjectWithLoc] alloc] initWithMutexMode:PTHREAD_MUTEX_RECURSIVE];

[myObject lock]; // when you need exclusive access to the object
[myObject unlock]; // when exclusive access to the object is no more needed

<强> NBSharedObjectWithLoc.h

//
//  NBSharedObjectWithLock.h
//  NBFoundation
//
//  Created by Nicolas Buquet on 03/06/2016.
//  Copyright © 2016 Nicolas Buquet. All rights reserved.
//

#import <Foundation/Foundation.h>
#include <pthread.h>

@interface NBSharedObjectWithLock : NSObject

- (instancetype)initWithMutexMode:(int)mutexMode;
// mutexMode can be:
//   - PTHREAD_MUTEX_NORMAL
//   - PTHREAD_MUTEX_ERRORCHECK
//   - PTHREAD_MUTEX_RECURSIVE
//   - PTHREAD_MUTEX_DEFAULT ( = PTHREAD_MUTEX_NORMAL )

- (BOOL)lock;
- (BOOL)unlock;

@end

<强> NBSharedObjectWithLoc.m

//
//  NBSharedObjectWithLock.m
//  NBFoundation
//
//  Created by Nicolas Buquet on 03/06/2016.
//  Copyright © 2016 Nicolas Buquet. All rights reserved.
//

#import "NBSharedObjectWithLock.h"

@implementation NBSharedObjectWithLock
{
    pthread_mutex_t _mutexLock;
}

- (instancetype)initWithMutexMode:(int)mutexMode
{
    // mutexMode can be:
    //   - PTHREAD_MUTEX_NORMAL
    //   - PTHREAD_MUTEX_ERRORCHECK
    //   - PTHREAD_MUTEX_RECURSIVE
    //   - PTHREAD_MUTEX_DEFAULT ( = PTHREAD_MUTEX_NORMAL )

    self = [super init];

    if (!self )
        return nil;

    pthread_mutexattr_t mutexAttributes;

    pthread_mutexattr_init(&mutexAttributes);
    pthread_mutexattr_settype(&mutexAttributes, mutexMode);
    pthread_mutex_init(&_mutexLock, &mutexAttributes);

    return self;
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutexLock);

    [super dealloc];
}

- (BOOL)lock
{
    return pthread_mutex_lock(&_mutexLock);
}

- (BOOL)unlock
{
    return pthread_mutex_unlock(&_mutexLock);
}

@end

使用这些基类,可以从一个线程访问对象。将锁上一把锁。如果另一个线程想要访问它,它将等待(线程将被暂停),直到释放对象的锁定。如果锁定在同一个线程上,则同一线程上的多个对象可以访问锁定的对象。

锁定/解锁调用会替换@synchronize块。 请注意,您不能在锁定调用和解锁调用之间返回,否则您的对象将永远不会被解锁。如果您需要返回一个值:

- (NSDictionary *)dictionaryFromLockedObject
{
    [myObject lock]
    NSDictionary *dict = [myObject callAMethodThatReturnsADictionary];
    [myObject unlock]

    return dict;
}