【问题标题】:CFReadStreamHasBytesAvailable polling - best practicesCFReadStreamHasBytesAvailable 轮询 - 最佳实践
【发布时间】:2009-02-28 22:01:08
【问题描述】:

我目前正在使用CFReadStreamHasBytesAvailable 轮询我的CFReadStream 以获取新数据。

(首先,一些背景知识:我正在做自己的线程,我不想/不需要弄乱 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 - 这很好,但我也在尝试阻止上传进度和下载进度,所以阻止那里不会'没有帮助...)。

任何见解将不胜感激 - 谢谢!

【问题讨论】:

  • 您没有单独的线程用于上传和下载?
  • 每个请求实际上都有自己的线程。
  • 也许有一些我不理解的要求,但如果上传和下载分配给他们自己的线程,那么问题会消失吗?也就是说,你只有一个线程读取流,一个线程写入流,每个线程阻塞。但是你需要同步它们。
  • 不需要两个线程,我可以从 ReadStream 获取我需要的所有信息(我不需要/有一个写流)。我正在从后台线程轮询 ReadStream,问题是什么是“好”的轮询方式?

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


【解决方案1】:

我认为这个问题有点自相矛盾,因为你问的是做一些本质上不是最佳实践的事情的最佳实践是什么;)

如果有一种非常好的阻止网络 I/O 的方法,那么根据定义,任何导致您轮询的妥协都不是最佳做法。

也就是说,如果您进行投票,我认为在您的线程上“运行 runloop 直到日期”可能更合适,而不是使用您想象的任何 posix sleep 或 yield 方法。请记住,每个线程都有自己的 runloop,因此基本上通过运行 runloop,您可以让 Apple 使用其最佳实践的概念来阻塞直到未来的某个日期。

至于时间延迟,我不知道你是否会得到一个确定的答案,什么是美好的时光。这是在 CPU 轮询周期与当 I/O 准备好从网络读取时卡在 runloop 中之间的权衡。

理想情况下,我认为我会将您的精力重新集中在使用 I/O 阻塞调用来完成这项工作上,但如果您坚持使用 poll & idle 技术,请不要太担心具体的延迟时间。只需选择有效且似乎不会对任一方向的性能产生负面影响的东西即可。

(另外,我想澄清一下,我对轮询与阻塞的事情并不太虔诚,我只是在强调它的价值,因为你显然在寻找一个提升的解决方案)。

【讨论】:

  • 你当然没有错 :)。我使用的是普通的 ol' 线程,没有与之关联的运行循环。我想我会避免运行循环的开销(可以忽略不计?)。我还认为轮询会在系统的某个级别发生,所以我不妨自己做——尽管我可能错了。
  • 我认为您认为轮询将在系统中的某个地方发生的假设是错误的。基于 runloop 的 CFStream 的工作方式是在实际等待网络 I/O 的某个时刻,它实际上变成了硬件驱动的唤醒。所以没有投票,只是等待。
  • 是的,你是对的。我想最好的方法是使用运行循环并安排一个计时器来定期检查上传状态。 (或者使用 CFRunLoopRunInMode 强制 runloop 定期将控制权交还给我 - 开销差异?)。
  • 不管怎样,我这样做的全部原因完全是烟消云散(除非我能想出一个解决办法)。无法准确获取小额传输的上传进度:devforums.apple.com/thread/9352?tstart=0
【解决方案2】:

在单独的线程上执行基于 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;
    }
}

【讨论】:

  • 自己处理状态不是与运行循环的目的相反吗?如果发生事件,它应该会通知您,对吧?
猜你喜欢
  • 1970-01-01
  • 2011-02-10
  • 1970-01-01
  • 2022-01-15
  • 2017-09-26
  • 2013-05-10
  • 1970-01-01
  • 1970-01-01
  • 2013-10-02
相关资源
最近更新 更多