我正在使用从外接设备接收数据。我想在一个线程中异步处理它的数据读/写队列。带GCD的异步NSStream I/O
我主要工作:有一个类简单地管理两个流,使用NSStreamDelegate
来响应传入数据,以及响应NSStreamEventHasSpaceAvailable发送失败后在缓冲区中等待的数据先发送。
这个班,我们称之为SerialIOStream
,不知道线程或GCD队列。相反,它的用户,我们称之为DeviceCommunicator
,使用GCD的队列中,它初始化SerialIOStream
类(这反过来又创造并打开流,在当前runloop调度它们):
ioQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(ioQueue, ^{
ioStreams = [[SerialIOStream alloc] initWithPath:[@"/dev/tty.mydevice"]];
[[NSRunLoop currentRunLoop] run];
});
这样一来, SerialIOStream
s stream:handleEvent:
方法显然在那个GCD队列中运行。
但是,这会导致一些问题。我相信我遇到并发问题,直到崩溃,主要是在将未完成的数据提供给输出流。有在我的缓冲输出数据传送到流代码的重要组成部分,然后看看有多少数据实际被接受了入流,然后除去该部分从我的缓冲区:
NSInteger n = self.dataToWrite.length;
if (n > 0 && stream.hasSpaceAvailable) {
NSInteger bytesWritten = [stream write:self.dataToWrite.bytes maxLength:n];
if (bytesWritten > 0) {
[self.dataToWrite replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
}
}
上面的代码可以从两个地方得到所谓:
- 从用户(
DeviceCommunicator
) - 从本地
stream:handleEvent:
方法,被告知有输出流中的空间之后。
这些可能(当然,是)在单独的线程中运行,因此我需要确保它们不会同时运行此代码。
我想发送出新的数据时,我想通过使用DeviceCommunicator
以下代码解决这个问题:
dispatch_async (ioQueue, ^{
[ioStreams writeData:data];
});
(writeData
添加数据到dataToWrite
,见上文,然后运行该发送上述代码它到流)
但是,这并不奏效,显然是因为ioQueue是一个并发队列,它可能决定使用任何可用的线程,因此当调用writeData
时会导致竞争条件还有一个来自的电话,在不同的线程上。
所以,我想我混合了对线程的期望(我更熟悉一点),因为我对GCD队列中的显然存在误解。
我该如何解决这个问题?
我可以添加一个NSLock,用它保护writeData方法,我相信这样可以解决这个问题。但我不太确定这就是GCD应该如何使用 - 我觉得这是一个混乱的印象。
难道我宁愿使用自己的串行队列来创建一个单独的类来访问和修改dataToWrite
缓冲区吗?
我仍然试图把握与此有关的模式。不知何故,它看起来像一个经典的制片人/消费者模式,但在两个层面上,我没有这样做。
唉 - 我不知道https://stackoverflow.com/a/31317588/43615是否是我寻求的答案。 –