CFReadStreamHasBytesAvailable polling - 最佳实践

时间:2009-02-28 22:01:08

标签: iphone cocoa cocoa-touch network-programming core-foundation

我目前正在使用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;
  }
}

usleepsched_yield“足够好”吗?在usleep中有一个“好”号码可以睡觉吗?

(另外:是的,因为这是在我自己的线程中运行,我可以阻止CFReadStreamRead - 这会很棒但我也试图阻止上传进度以及下载进度,所以阻塞没有帮助......)。

非常感谢任何见解 - 谢谢!

2 个答案:

答案 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;
    }
}