使用以下新代码编辑
我在多线程方面相对较新,但为了实现我的目标,快速完成并学习新东西,我决定使用多线程应用程序。
目标:从文件中解析大量字符串,并使用CoreData将每个单词保存到SQLite数据库中。 很大,因为单词的数量大约是300.000 ......
所以这是我的方法。
步骤1.将所有单词解析到文件中,将其放入一个巨大的NSArray中。 (快点完成)
步骤2.创建插入NSBlockOperation的NSOperationQueue。
主要问题是这个过程很快就开始了,但很快就会减速。我使用NSOperationQueue,最大并发操作设置为100.我有一个Core 2 Duo Process(双核没有HT)。
我看到使用NSOperationQueue会产生很多开销来创建NSOperation(停止调度队列需要大约3分钟来创建300k NSOperation。) 当我开始调度队列时,CPU达到170%。
我尝试删除NSOperationQueue并使用GDC(300k循环是瞬时完成的(注释行))但是使用的cpu只有95%,问题与NSOperations相同。很快这个过程就慢了。
做得好的一些提示?
这里有一些代码(原始问题代码):
- (void)inserdWords:(NSArray *)words insideDictionary:(Dictionary *)dictionary {
NSDate *creationDate = [NSDate date];
__block NSUInteger counter = 0;
NSArray *dictionaryWords = [dictionary.words allObjects];
NSMutableSet *coreDataWords = [NSMutableSet setWithCapacity:words.count];
NSLog(@"Begin Adding Operations");
for (NSString *aWord in words) {
void(^wordParsingBlock)(void) = ^(void) {
@synchronized(dictionary) {
NSManagedObjectContext *context = [(PRDGAppDelegate*)[[NSApplication sharedApplication] delegate] managedObjectContext];
[context lock];
Word *toSaveWord = [NSEntityDescription insertNewObjectForEntityForName:@"Word" inManagedObjectContext:context];
[toSaveWord setCreated:creationDate];
[toSaveWord setText:aWord];
[toSaveWord addDictionariesObject:dictionary];
[coreDataWords addObject:toSaveWord];
[dictionary addWordsObject:toSaveWord];
[context unlock];
counter++;
[self.countLabel performSelectorOnMainThread:@selector(setStringValue:) withObject:[NSString stringWithFormat:@"%lu/%lu", counter, words.count] waitUntilDone:NO];
}
};
[_operationsQueue addOperationWithBlock:wordParsingBlock];
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// dispatch_async(queue, wordParsingBlock);
}
NSLog(@"Operations Added");
}
提前谢谢。
编辑...
感谢Stephen Darlington,我重写了我的代码,并找出了问题所在。最重要的是:不要在线程之间共享CoreData对象 ...这意味着不要混合使用不同上下文检索的Core数据对象。
这让我使用@synchronized(字典)导致慢动作代码执行! 我只使用MAXTHREAD实例删除了大量的NSOperation创建。 (2或4而不是300k ......是一个巨大的差异)
现在我可以在30/40秒内解析300k + String。令人印象深刻! 我仍然有一些问题(接缝解析更多的单词,而不是只有1个线程,如果线程超过1,它解析不是所有单词......我需要解决它)但现在代码非常有效。也许下一步可能是使用OpenCL并将其注入GPU:)
这里是新代码
- (void)insertWords:(NSArray *)words forLanguage:(NSString *)language {
NSDate *creationDate = [NSDate date];
NSPersistentStoreCoordinator *coordinator = [(PRDGAppDelegate*)[[NSApplication sharedApplication] delegate] persistentStoreCoordinator];
// The number of words to be parsed by the single thread.
NSUInteger wordsPerThread = (NSUInteger)ceil((double)words.count / (double)MAXTHREADS);
NSLog(@"Start Adding Operations");
// Here I minimized the number of threads. Every thread will parse and convert a finite number of words instead of 1 word per thread.
for (NSUInteger threadIdx = 0; threadIdx < MAXTHREADS; threadIdx++) {
// The NSBlockOperation.
void(^threadBlock)(void) = ^(void) {
// A new Context for the current thread.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coordinator];
// Dictionary now is in accordance with the thread context.
Dictionary *dictionary = [PRDGMainController dictionaryForLanguage:language usingContext:context];
// Stat Variable. Needed to update the UI.
NSTimeInterval beginInterval = [[NSDate date] timeIntervalSince1970];
NSUInteger operationPerInterval = 0;
// The NSOperation Core. It create a CoreDataWord.
for (NSUInteger wordIdx = 0; wordIdx < wordsPerThread && wordsPerThread * threadIdx + wordIdx < words.count; wordIdx++) {
// The String to convert
NSString *aWord = [words objectAtIndex:wordsPerThread * threadIdx + wordIdx];
// Some Exceptions to skip certain words.
if (...) {
continue;
}
// CoreData Conversion.
Word *toSaveWord = [NSEntityDescription insertNewObjectForEntityForName:@"Word" inManagedObjectContext:context];
[toSaveWord setCreated:creationDate];
[toSaveWord setText:aWord];
[toSaveWord addDictionariesObject:dictionary];
operationPerInterval++;
NSTimeInterval endInterval = [[NSDate date] timeIntervalSince1970];
// Update case.
if (endInterval - beginInterval > UPDATE_INTERVAL) {
NSLog(@"Thread %lu Processed %lu words", threadIdx, wordIdx);
// UI Update. It will be updated only by the first queue.
if (threadIdx == 0) {
// UI Update code.
}
beginInterval = endInterval;
operationPerInterval = 0;
}
}
// When the NSOperation goes to finish the CoreData thread context is saved.
[context save:nil];
NSLog(@"Operation %lu finished", threadIdx);
};
// Add the NSBlockOperation to queue.
[_operationsQueue addOperationWithBlock:threadBlock];
}
NSLog(@"Operations Added");
}
答案 0 :(得分:2)
一些想法:
NSManagedObjectContext
。这不好简而言之,即使你使用像GCD这样的东西,线程也很难。
答案 1 :(得分:0)
如果没有测量和分析,这很难实现,但对我来说可疑的是你保存到目前为止保存的每个单词的完整单词词典。因此,每次保存的数据量会越来越大。
// the dictionary at this point contains all words saved so far
// which each contains a full dictionary
[toSaveWord addDictionariesObject:dictionary];
// add each time so it gets bigger each time
[dictionary addWordsObject:toSaveWord];
因此,每次保存都会节省越来越多的数据。为什么用每个单词保存所有单词的字典?
其他一些想法:
要尝试的事情: