CFReadStreamHasBytesAvailable 轮询 - 最佳实践

发布于 2024-07-14 06:48:29 字数 900 浏览 4 评论 0原文

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

(首先,一些背景:我正在做自己的线程,我不想/不需要弄乱运行循环的东西,所以客户端回调的东西在这里并不真正适用)。

我的问题是:可接受的民意调查做法是什么?

Apple 文档 对这个主题似乎没有太大帮助。

他们建议“在等待的时候做点别的事情”。 我目前正在做一些事情:

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 - 这会很棒,但我也试图获取上传进度和下载进度,所以阻塞没有帮助...)。

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

I'm currently polling my CFReadStream for new data with CFReadStreamHasBytesAvailable.

(First, some background: I'm doing my own threading and I don't want/need to mess with runloop stuff, so the client callback stuff doesn't really apply here).

My question is: what are accepted practices for polling?

Apple's documentation on the subject doesn't seem too helpful.

They recommend to "do something else while you wait". I'm currently just doing something along the lines of:

while(!done)
{
  if(CFReadStreamHasBytesAvailable(readStream))
  {
    CFReadStreamRead(...) ... bla bla bla
  } else {
    usleep(3600); // I made this up
    sched_yield(); // also made this up
    continue;
  }
}

Is the usleep and the sched_yield "good enough"? In there a "good" number to sleep for in usleep?

(Also: yes, because this is running in my own thread, I could just block on CFReadStreamRead - which would be great but I'm also trying to snag upload progress as well as download progress, so blocking there wouldn't help...).

Any insight would be much appreciated - thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

极度宠爱 2024-07-21 06:48:29

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

当有一个完美的方法来阻止网络 I/O 时,任何导致你的妥协根据定义,轮询并不是最佳实践。

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

至于延迟时间,不知道你是否能得到一个确定的答案,什么是美好时光。 这是让 CPU 充满轮询周期与在 I/O 准备好从网络读取时陷入运行循环一段时间之间的权衡。

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

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

I think this question is a bit of a paradox because you're asking what the best practices are for doing something that's intrinsically not a best practice ;)

When there's a perfectly good method for blocking on network I/O, any compromise that causes you to poll instead is by definition not the best practice.

That said, if you do poll I think it might be more appropriate to "run the runloop until date" on your thread, instead of using whatever posix sleep or yield method you're imagining. Remember that each thread gets its own runloop, so essentially by running the runloop you're allowing Apple to employ its concept of best practices for blocking until a future date.

As for the time delay, I don't know if you'll get a definitive answer for what a good time is. It's a tradeoff between peppering the CPU with polling cycles vs. being stuck in the runloop for a little while when I/O is ready to be read from the network.

Ideally I think I would refocus your efforts on making this work using I/O blocking calls, but if you stick with the poll & idle technique, don't fret too much about the specific delay time. Just pick something that works and doesn't seem to impact performance negatively in either direction.

(Also, I'd like to clarify that I'm not too religious about the polling vs. blocking thing, I'm only stressing its value because you're obviously in search of an elevated solution).

孤单情人 2024-07-21 06:48:29

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

When doing manual CFStream based connections on a separate thread (for custom things like bandwidth monitoring and throttling), I use a combination of CFReadStreamScheduleWithRunLoop, CFRunLoopRunInMode and CFReadStreamSetClient. Basically I run for 0.25 seconds and then check stream status. The client callback also gets notified on its own as well. This allows me to periodically check read status and do some custom behavior but rely mostly on (stream) events.

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文