我目前正在使用CFReadStream
轮询我的CFReadStreamHasBytesAvailable
新数据。
(首先,一些背景:我正在做自己的线程,我不想/需要搞乱runloop的东西,所以客户端回调的东西在这里并不适用)。
我的问题是:什么是公认的民意调查方法?
关于这个问题的Apple's documentation似乎没什么帮助。
他们建议“在等待的时候做点什么”。我现在正在做一些事情:
while(!done)
{
if(CFReadStreamHasBytesAvailable(readStream))
{
CFReadStreamRead(...) ... bla bla bla
} else {
usleep(3600); // I made this up
sched_yield(); // also made this up
continue;
}
}
usleep
和sched_yield
“足够好”吗?在usleep
中有一个“好”号码可以睡觉吗?
(另外:是的,因为这是在我自己的线程中运行,我可以阻止CFReadStreamRead
- 这会很棒但我也试图阻止上传进度以及下载进度,所以阻塞没有帮助......)。
非常感谢任何见解 - 谢谢!
答案 0 :(得分:4)
我认为这个问题有点悖论,因为你问的是做一些本质上不是最佳做法的最佳做法是什么;)
如果有一种非常好的阻止网络I / O的方法,那么导致您进行轮询的任何妥协都不是最佳做法。
那就是说,如果你进行民意调查,我认为在你的线程上“运行runloop到date”可能更合适,而不是使用你想象的任何posix sleep或yield方法。请记住,每个线程都有自己的runloop,所以基本上通过运行runloop,你可以让Apple在最后的日期之前使用它的最佳实践概念。
至于时间延迟,我不知道你是否会得到一个好时光的明确答案。当I / O准备好从网络中读取时,这需要在CPU与轮询周期之间进行权衡,而不是在运行环路中停留一段时间。
理想情况下,我认为我会重新集中精力,使用I / O阻止调用来完成这项工作,但如果你坚持使用民意调查&闲置技术,不要过多担心具体的延迟时间。只需挑选一些有效的东西,似乎不会对任何方向产生负面影响。
(另外,我想澄清一下,我对于投票与封锁事情并不太虔诚,我只是强调它的价值,因为你显然正在寻找一个更高的解决方案)。
答案 1 :(得分:1)
当在单独的线程上进行基于CFStream的手动连接时(对于带宽监视和限制等自定义事项),我使用CFReadStreamScheduleWithRunLoop,CFRunLoopRunInMode和CFReadStreamSetClient的组合。基本上我运行0.25秒然后检查流状态。客户端回调也会自行获得通知。这允许我定期检查读取状态并执行一些自定义行为,但主要依赖于(流)事件。
static const CFOptionFlags kMyNetworkEvents =
kCFStreamEventOpenCompleted
| kCFStreamEventHasBytesAvailable
| kCFStreamEventEndEncountered
| kCFStreamEventErrorOccurred;
static void MyStreamCallBack(CFReadStreamRef readStream, CFStreamEventType type, void *clientCallBackInfo) {
[(id)clientCallBackInfo _handleNetworkEvent:type];
}
- (void)connect {
...
CFStreamClientContext streamContext = {0, self, NULL, NULL, NULL};
BOOL success = CFReadStreamSetClient(readStream_, kMyNetworkEvents, MyStreamCallBack, &streamContext);
CFReadStreamScheduleWithRunLoop(readStream_, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if (!CFReadStreamOpen(readStream_)) {
// Notify error
}
while(!cancelled_ && !finished_) {
SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, NO);
if (result == kCFRunLoopRunStopped || result == kCFRunLoopRunFinished) {
break;
}
if (([NSDate timeIntervalSinceReferenceDate] - lastRead_) > MyConnectionTimeout) {
// Call timed out
break;
}
// Also handle stream status CFStreamStatus status = CFReadStreamGetStatus(readStream_);
if (![self _handleStreamStatus:status]) break;
}
CFRunLoopStop(CFRunLoopGetCurrent());
CFReadStreamSetClient(readStream_, 0, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(readStream_, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFReadStreamClose(readStream_);
}
- (void)_handleNetworkEvent:(CFStreamEventType)type {
switch(type) {
case kCFStreamEventOpenCompleted:
// Notify connected
break;
case kCFStreamEventHasBytesAvailable:
[self _handleBytes];
break;
case kCFStreamEventErrorOccurred:
[self _handleError];
break;
case kCFStreamEventEndEncountered:
[self _handleBytes];
[self _handleEnd];
break;
default:
Debug(@"Received unexpected CFStream event (%d)", type);
break;
}
}