Objective-C中与Java的BlockingQueue相当的内容是什么?

4
我刚开始学习iPhone开发,之前从事Java开发很多年。我正在寻找Objective-C相当于Java的BlockingQueue。有类似的东西吗?
如果我走错了路,以下是我的目标:
我想一次显示从网络服务器获取的数据块。为了让用户不注意到网络延迟,我希望始终预先获取一些数据块。在Java领域中,我会在获取线程和显示线程之间使用线程安全队列。
3个回答

8
以下是一个带有队列和出队方法的阻塞队列实现。预期的情况是,一个线程进入循环调用 dequeueUnitOfWorkWaitingUntilDate: 方法并处理工作单元,而第二个线程则调用 queueUnitOfWork: 方法将工作单元加入队列。
@interface MyBlockingQueue : NSObject {
    NSMutableArray *queue;
    NSConditionLock *queueLock;
}
- (id)dequeueUnitOfWorkWaitingUntilDate:(NSDate *)timeoutData;
- (void)queueUnitOfWork:(id)unitOfWork;
@end

enum {
    kNoWorkQueued = 0,
    kWorkQueued = 1
}

@implementation MyBlockingQueue
- (id)init {
    if ((self = [super init])) {
        queueLock = [[NSConditionLock alloc] initWithCondition:kNoWorkQueued];
        workItems = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)dealloc {
    [queueLock release];
    [workItems release];
    [super dealloc];
}

- (id)dequeueUnitOfWorkWaitingUntilDate:(NSDate *)timeoutDate {
    id unitOfWork = nil;
    if ([queueLock lockWhenCondition:kWorkQueued beforeDate:timeoutDate]) {
        unitOfWork = [[[queue objectAtIndex:0] retain] autorelease];
        [queue removeObjectAtIndex:0];
        [queueLock unlockWithCondition:([workItems count] ? kWorkQueued : kNoWorkQueued)];
    }
    return unitOfWork;
}

- (void)queueUnitOfWork:(id)unitOfWork {
    [queueLock lock];
    [queue addObject:unitOfWork];
    [queueLock unlockWithCondition:kWorkQueued];
}
@end

你必须检查lockWhenCondition:beforeDate:的返回值,否则如果时间过期而未获取锁,则几乎肯定会在队列上引发IndexOutOfBounds异常。另外,在queueUnitOfWork:中,theConditionLock应该是queueLock,对吧? - Jason Coco
实际上,当您解锁时,也会抛出异常,因为您从一开始就没有真正获取锁。 - Jason Coco
当然,我的疏忽了。谢谢你指出来。已经修复了。 - Jon Hess

2
您可以简单地启动一个NSOperation并在数据返回时发布通知(完成加载)。请查看Dave Dribin关于使用NSOperation进行并发的博客文章,其中展示了如何封装一个NSURLConnection会话。

http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/

如果你不是在讨论需要使用 NSURLConnection 的访问网络服务或网站,而是直接使用 TCP/IP 或 UDP,那么你可以使用 Cocoa Async Socket。

http://code.google.com/p/cocoaasyncsocket/

最好的祝福,

感谢您发布了Dave Dribin博客的链接。那救了我的命。我一直在异步方法中运行空闲循环以保持在后台线程中。 - Ben

1

我认为原生的东西不存在 - 你可能需要编写自己的类来维护网络对象的队列。你的头文件可能看起来像这样:

@interface ObjcBlockingQueue : NSObject {
    // The objects that you're holding onto
    NSArray *objects;
}

@property(nonatomic,retain) NSArray *objects;

- (ServerData *)getNextChunk;

然后您可以实现getNextChunk,以弹出并返回objects数组中的顶部对象,如果[objects count]小于某个值,则启动一个线程来获取一些更多的对象(可能使用NSURLConnection,ObjcBlockingQueue作为委托)。您还可以将该线程/连接启动在重写的init方法内以填充队列。
您可能还想考虑添加一个...
- (BOOL)isChunkAvailable;

这是一个方法,它可以让你的显示线程知道它是否可以立即显示新内容,或者是否需要显示加载消息。根据你显示数据的位置和应用程序的结构,将ObjcBlockingQueue作为单例类也可能值得一试。


太棒了。感谢如此迅速的回答。我还不太明白线程安全是怎么工作的。在Java中,像ArrayList这样的基本数据容器不是线程安全的,而且由于NsMutableArray文档没有说明,我认为它也不是线程安全的。那么,你只需在NSMutableArray上执行@syncronized吗? - William Pietri
1
这是一个很好的答案,但它需要相当好的 C 线程理解,这与 Java 有些不同。在您编写自己的类以重新实现 Java 中的某些东西之前,我建议您考虑使用 NSOperation 子类和 NSOperation 队列。可以在 NSOperation 完成时收到通知,在此时访问某些属性(例如包含该操作完全加载的数据的数据属性)将是安全的。 - Jason Coco
@William:这肯定是一种可能性。如果你感觉可以的话,你也可以在对象上管理自己的锁。另一个选择是像@Jason所说的那样,使用NSOperation子类和队列。请参阅“Objective-C编程语言:线程”(http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocThreading.html)和线程编程指南(http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html#//apple_ref/doc/uid/10000057i-CH1-SW1)。 - Tim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接