0

dispatch queues同时运行两个不同的对象,循环 365 次迭代并实例化一个对象。该循环将对象添加到NSNotificationCenter,然后对象在其事件存储异步代码块完成后发布通知。我遇到的问题是我应该在调试器中收到 730 条消息,但我没有。每次运行应用程序时,我都会收到不同数量的消息,从 513 到 630。

发生这种情况有什么原因吗?

这是我执行循环并将对象添加到Notification Center. 我创建了一个HZCalendarDay添加到不可变数组的实例。

- (id)init {
    self = [super init];

    if (self) {
        // Alloc / Init instance variables.
        previousDays = [[NSMutableArray alloc] init];
        futureDays = [[NSMutableArray alloc] init];
        self.datesOnCalendar = [[NSMutableArray alloc] init];
        self.stateOfCalendarCache = StateOfEventStoreCache_CachingRequired;
        self.currentDay = [[HZCalendarDay alloc] init];

        // Setup our event store security access.
        [self setupEventStore];

        DEDateUtility *dateUtility = [[DEDateUtility alloc] init];
        NSDate *today = [dateUtility normalizedDateWithDate:[NSDate date]];
        HZCalendarDay *date = [[HZCalendarDay alloc] initOnDate:today withEventStore:self.eventStore];
        [self.datesOnCalendar addObject:date];
        self.currentDay = date;

        // Before we start caching, we need to setup the KVO so we can compile the
        // completed caches, since both previous and future days are cached separately.


        // Start caching previous days.
        dispatch_queue_t previousDaysCacheQueue = dispatch_queue_create("previousDaysCacheQueue", NULL);
        dispatch_async(previousDaysCacheQueue, ^ {
            int numberOfDays = (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE * 365)/2;
            for (int count = 1; count < numberOfDays; count++) {
                NSDate *previousDate = [dateUtility adjustDate:today byNumberOfDays:-count];
                HZCalendarDay *calendarDay = [[HZCalendarDay alloc] initOnDate:previousDate withEventStore:self.eventStore];
                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(completeCaching:) name:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:calendarDay];

                @synchronized (self.datesOnCalendar) {
                    [self.datesOnCalendar insertObject:calendarDay atIndex:0];
                }
            }
        });

        // Start caching future days.
        dispatch_queue_t futureDaysCacheQueue = dispatch_queue_create("futureDaysCacheQueue", NULL);
        dispatch_async(futureDaysCacheQueue, ^ {
            int numberOfDays = (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE * 365)/2;
            for (int count = 1; count < numberOfDays; count++) {
                NSDate *futureDate = [dateUtility adjustDate:today byNumberOfDays:count];
                HZCalendarDay *calendarDay = [[HZCalendarDay alloc] initOnDate:futureDate withEventStore:self.eventStore];
                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(completeCaching:) name:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:calendarDay];

                @synchronized (self.datesOnCalendar) {
                    [self.datesOnCalendar addObject:calendarDay];
                }
            };
        });
    }

    return self;
}

在同一个类中,我有我NSNotificationCenter发布到的方法,以及cacheStage NSLogs当前值的设置器。它最终应该等于 730,但它永远不会那么高。

- (void)setCachingStage:(NSInteger)cachingStage {
    _cachingStage = cachingStage;
    NSLog(@
          "Cache Stage: %d", _cachingStage);

    if (_cachingStage == (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE*365)) {
        self.stateOfCalendarCache = StateOfEventStoreCache_CachingComplete;
        NSLog(@"Caching completed.");
    }
}

- (void)completeCaching:(NSNotification *)notification {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:(HZCalendarDay *)notification.object];
    self.cachingStage++;
}

这是 的初始化程序HZCalendarDay,然后获取事件和提醒的缓存。设置事件和提醒后,将其Posts添加到NotificationCenter.

- (void)setCacheStage:(NSInteger)cacheStage {
    _cacheStage = cacheStage;

    if (_cacheStage == 2) {
        [[NSNotificationCenter defaultCenter] postNotificationName:HZ_KVO_CALENDAR_CACHE_NOTIFICATION object:self];
    }
}

- (id)initOnDate:(NSDate *)date withEventStore:(EKEventStore *)eventStore {
    self = [super init];

    if (self) {
        self.events = [[NSArray alloc] init];
        self.reminders = [[NSArray alloc] init];
        self.date = date;
        self.eventStore = eventStore;

        [self fetchAllEvents];
        [self fetchAllReminders];
    }

    return self;
}

- (void)fetchAllEvents {
    NSPredicate *fetchPredicateForEvents = [self.eventStore predicateForEventsWithStartDate:[self startTime] endDate:[self endTime] calendars:[self.eventStore calendarsForEntityType:EKEntityTypeEvent]];
    self.events = [self.eventStore eventsMatchingPredicate:fetchPredicateForEvents];

    // Don't store a nil array in the dictionary.
    if (!self.events) {
        self.events = [[NSArray alloc] init];
    }

    self.cacheStage++;
}

- (void)fetchAllReminders {
    NSPredicate *fetchPredicateForReminders = [self.eventStore predicateForIncompleteRemindersWithDueDateStarting:[self startTime] ending:[self endTime] calendars:[self.eventStore calendarsForEntityType:EKEntityTypeReminder]];
    [self.eventStore fetchRemindersMatchingPredicate:fetchPredicateForReminders completion:^(NSArray *reminders) {
        @synchronized (self.reminders) {
            self.reminders = reminders;
        }
        self.cacheStage++;

    }];
}

有人可以向我解释可能出了什么问题吗?这与多线程有关吗?如果是这样,我是否有更好的方法让实例化对象的类知道对象已缓存事件存储提醒?此类是我的 UITableView 的数据源,因此我需要缓存提醒,并以某种方式让我的数据源知道缓存已完成。这允许我的 UI 在缓存发生时在其上显示“刷新”或加载指示器。

4

1 回答 1

0

您实现的方式cachingStage不是线程安全的。请注意,在这种情况下,仅包装cachingStage/ setCachingStage:in的实现@synchronized是不够的,因为您这样做:

self.cachingStage++;

...实际上相当于:

NSInteger foo = self.cacheStage; foo++; self.cacheStage = foo;

当您使用两个线程 A 和 B 执行此操作时,可能会发生以下情况:

  1. 线程 A 将当前值 , 读xfoo
  2. 线程 A 被抢占并进入睡眠状态。
  3. 线程 B 读取当前值 ,xfoo, 递增foox + 1, 并将其写回cachingStage, 做一堆其他的事情,最终被抢占。
  4. 线程 A 恢复,将其递增foox + 1,并将该值写回cachingStage

在这一点上,您期望cachingStagex + 2,但事实并非如此。是x + 1。如果你仔细想想,它就完美地解释了为什么你的价值从来没有达到你想象的那么高。您需要明确保护可以从多个线程操作的任何数据/状态。而不是setCachingStage:,在这种情况下,您可能想要一个类似的方法incrementCachingStage,然后您可以像这样实现:

- (void) incrementCachingStage {
    @synchronized(self) {
        _cachingStage++;
        NSLog(@"Cache Stage: %d", _cachingStage);

        if (_cachingStage == (HZ_NUMBER_OF_TOTAL_YEARS_TO_CACHE*365)) {
            self.stateOfCalendarCache = StateOfEventStoreCache_CachingComplete;
            NSLog(@"Caching completed.");
        }
    }
}

不过,这只是一条数据,而且这里有一堆数据,其中一些/全部可能需要设为线程安全的。

于 2013-08-08T17:44:26.773 回答