线程机制:在后台准备和释放缓存

时间:2009-11-26 20:52:53

标签: iphone multithreading locking

前言:这已成为一个相当长的帖子。虽然我不是编程的新手,但我在线程方面几乎没有实践,可能需要一些帮助......

这可能是一个常见的问题,可以用短语来描述,但我有点不知所措。

有些背景......

我正在为iPhone编写代码,因为机器运行缓慢导致性能问题。我正在尝试优化以保持UI的活泼,目前我正在考虑添加一些线程。

想象一下:我有一个用户可以搜索的大型数据库。为了搜索,用户切换到特定视图,在那里他获得一个编辑框以输入他的搜索文本。每次用户在搜索框中键入字符时,都会以同步运行搜索,并立即显示结果。

最初,数据是在sqlite数据库中,虽然搜索已经准备就绪,但搜索总是花费几秒钟,即使我在一个线程中运行搜索,UI也会感觉迟钝,并且只更新结果列表一次搜索在几秒钟后完成。

所以我把搜索代码改为快得多,现在低于十分之一秒,这意味着我在搜索输入时不再有任何延迟。

问题是,为了快速搜索,我需要做一些冗长的准备才能开始搜索。这个准备需要1-2秒。它会在内存中创建大量对象,如果不需要,我不想保留它们。

所以我在搜索视图出现期间在一个线程中运行准备工作。其中大部分都是动画的,因此准备工作可以在平时完成,而用户甚至不会注意到。

如果卸载了搜索视图,我需要再次释放缓存。这也需要一段时间(最新型号约为1/2),所以我也想在一个线程中执行此操作,否则切换到另一个视图会有明显的延迟。

所有这一切看起来并不困难。我有两个函数来准备和释放缓存,如下所示:

- (void) internalPrepareCache
{
 NSAutoreleasePool *pool = nil;
 if (![NSThread isMainThread]) pool = [[NSAutoreleasePool alloc] init];
 [cacheLock lock];
 if (!cacheReady) {
  Load Data Cache...; // can take a while
  cacheReady = true;
 }
 [cacheLock unlock];
 [pool release];
}

- (void) internalReleaseCache
{
 NSAutoreleasePool *pool = nil;
 if (![NSThread isMainThread]) pool = [[NSAutoreleasePool alloc] init];
 [cacheLock lock];
 if (!cacheReady) {
  Release Data Cache...; // can take a while
  cacheReady = false;
 }
 [cacheLock unlock];
 [pool release];
}

然后是视图控制器从主线程调用的函数:

// this gets called by the view controller when loaded:
- (void) threadedPrepareCache
{
 [NSThread detachNewThreadSelector:@selector(internalPrepareCache) toTarget:self withObject:nil];
}

// this gets called by the view controller upon unload:
- (void) threadedReleaseCache
{
 [NSThread detachNewThreadSelector:@selector(internalReleaseCache) toTarget:self withObject:nil];
}

// this gets called by the view controller to perform a search
- (void) searchUsingCache:...
{
 [self internalPrepareCache];
 Perform the search ...
}

正如代码所示,我使用的是一个全局NSLock对象,用于围绕缓存准备和缓存释放代码包装锁定和解锁调用。我还有一个全局状态变量来告诉缓存是否准备就绪。

由于两种特殊情况,它变得复杂:

1。)用户可以非常快速地反复切换搜索视图。这可以排队几个缓存准备和缓存释放操作,直到开始准备缓存而用户的最后一个操作是要解除搜索。我想避免这种情况。

2.。)如果用户速度很快(或者iPhone非常慢)并且在缓存准备线程完成之前已经进入搜索,则非线程搜索需要等待缓存准备就绪。我担心这也可能与(1)的排队行动发生冲突。

3.)我做了以下测试:

[self threadedPrepareCache];
[self threadedReleaseCache];
[self threadedPrepareCache];
[self threadedReleaseCache];

此测试显示未遵循我的预定顺序:首先发布(当没有任何内容发布时)。这是一个极端的例子,但它告诉我,我的上述情况可能还没有正确编程,当我打算在最后一次调用时释放它时,我最终可能会做最后的准备。

我该如何解决这个问题?

我正在考虑使用另一个全局var来声明当前所需的缓存状态:它由主线程的函数设置,这些函数要求准备或丢弃缓存。然后,两个线程函数中的受锁保护的代码检查当前所需的状态并相应地动作。这样可以防止局势(1)的不必要的局限,对吧?但是我如何确保周围没有竞争条件呢?没有必要锁定这个期望状态var的设置,是吗?

我是否必须担心(2)?目前,搜索功能总是同步调用 internalPrepareCache (来自主线程),因此等待它准备就绪。这样安全吗?

2 个答案:

答案 0 :(得分:1)

道歉,如果这是基础,即使我多次阅读你的帖子。

我认为您想要争取的是以下内容:

  • 将缓存准备/细分工作保持在主线程之外,因为必须在主线程中完成UI体验,并且您不希望将其陷入困境。

  • 能够在完成之前发信号通知缓存准备线程,它需要在完成之前中止,因为变幻无常的用户决定返回,无论如何......并且让准备线程负责自行清理,因为它正在通过在同一个线程中调用清理内容或者自己完成工作来中止。

因此尝试使用[myObj performSelectorInBackground ...]来启动缓存准备,并且该线程方法应该监视一个属性,您可以从UI代码中设置该属性,以告知它在视图卸载时停止/中止。展开(清理缓存)时,也可以在后台执行该操作,但要考虑设置是否已中止并将自行清理。

最终,昂贵的东西应该脱离主线程,并在UI和支持线程之间建立适当的信号。

答案 1 :(得分:0)

Apple的“并发编程指南”最终帮助了我,特别是操作队列的概念,带有“序列化调度”:

我需要的是一个单一任务,它有一个要执行的操作队列(准备发布),因此它按顺序执行。

我还需要一种方法来检查当前操作是否是 prepare 操作。最后,我在prepare函数中添加了一些检查,以检查是否要求它提前取消其工作。

我将它与一个等待完成操作和清除当前队列的函数结合起来,现在我已经有了一个很好的解决方案。

我想在这里写下我的问题已经是解决方案的一半了。 所以,谢谢你的耐心;)