mac 개발 시리즈 31: 스레드 동기화 잠금 @synchronized 원본 이해
NSMutableArray *arr;
@synchronized(arr) {
arr = [self func]; // func DB
if(arr == nil) {
arr = [NSMutableArray array];
}
}
그런데 여기 분명히 동기화 자물쇠 @synchronized를 사용했는데 왜 여러 라인이 동시에 Block에 들어갑니까?기존의 방식대로 다시 쓰면 다음과 같은 C++ 구현이 가능합니다.
static void _I_Demo_synchronizedTest(Demo * self, SEL _cmd) {
NSMutableArray *arr;
{
id _sync_obj = (id)arr;
objc_sync_enter(_sync_obj); // , arr
try {
struct _SYNC_EXIT {
_SYNC_EXIT(id arg) : sync_exit(arg) {}
~_SYNC_EXIT() {objc_sync_exit(sync_exit); // , arr
}
id sync_exit;
} _sync_exit(_sync_obj);// , arr
} catch (id e) {
}
}
}
추가, objc 보기sync_enter와 objcsync_exit의 원본 코드는 다음과 같습니다.
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
// obj SyncData ,id2data
SyncData* data = id2data(obj, ACQUIRE);//
result = recursive_mutex_lock(&data->mutex); }
else
{ // @synchronized(nil) does nothing
}
return result;
}
다음:
int objc_sync_exit(id obj)
{ int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, RELEASE); //
result = recursive_mutex_unlock(&data->mutex);
} else {
// @synchronized(nil) does nothing
}
return result;
}
위의 원본에서 알 수 있듯이 1. @synchronized는 귀속 자물쇠를 사용합니다(즉 같은 라인은 다시 들어갈 수 있고 자물쇠가 사라지지 않습니다).2. @synchronized(nil)는 잠기지 않습니다. 이어서 다음과 같은 관건적인 데이터 구조를 보십시오. 분명히 SyncList는 단일 체인 테이블이고 SyncData는 단일 체인 테이블 노드이며 전체 저장은'지퍼법 해시 테이블'입니다.
typedef struct SyncData {
struct SyncData* nextData; // SyncData
DisguisedPtr object; // @synchronized obj
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex; //
} SyncData;
struct SyncList {
SyncData *data; //
spinlock_t lock; //
SyncList() : data(nil) { }
};
define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap sDataLists; // ,key:obj,value:
// obj SyncData static SyncData* id2data(id object, enum usage why)
{
spinlock_t *lockp = &LOCK_FOR_OBJ(object); // SyncList
SyncData **listp = &LIST_FOR_OBJ(object); // obj SyncData
SyncList SyncData* result = NULL;// cache
lockp->lock();
{
SyncData* p;
SyncData* firstUnused = NULL;
//
for (p = *listp; p != NULL; p = p->nextData) {
if ( p->object == object ) {
// obj SyncData
result = p;
// SyncData 1
OSAtomicIncrement32Barrier(&result->threadCount);
goto done;
}
// SyncData , ,
if ( (firstUnused == NULL) && (p->threadCount == 0) )
firstUnused = p;
}
// obj SyncData , SyncData
// an unused one was found, use it
if ( firstUnused != NULL ) {
result = firstUnused;
result->object = (objc_object *)object;
result->threadCount = 1;
goto done;
}
}
// obj SyncData , SyncData
result = (SyncData*)calloc(sizeof(SyncData), 1);
result->object = (objc_object *)object;
result->threadCount = 1;
new (&result->mutex) recursive_mutex_t();
// SyncData
result->nextData = *listp;
*listp = result;
done:
lockp->unlock();
return result;}
}
template
class StripedMap {
#if TARGET_OS_EMBEDDED
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };#endif
static unsigned int indexForPointer(const void *p) {
// obj index
uintptr_t addr = reinterpret_cast(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
};
@synchronized의 원본 코드가 실현되었는지 확인하고 crash를 돌아보면 문제는 주로 두 가지가 있다. 첫째,arr가 초기화되지 않았을 때nil이고 동기화 자물쇠가 효력이 발생하지 않았으며block은 임계 구역이 아니다.2. arr가 수정되었다. 즉, 메모리 주소와 비상량이다. 라인 1은 arr에 대응하는 주소가addr1이고 Block에 들어간다.루틴 2에서arr에 대응하는 주소는addr2로 받을 수 있으며, 마찬가지로 Block에 들어갈 수 있으며, 루틴 1이 Block을 실행하기를 기다리지 않습니다.
참조 링크:https://opensource.apple.com/source/objc4/objc4-680/runtime/objc-sync.mm https://github.com/opensource-apple/objc4/blob/master/runtime/objc-private.h
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.