CFReadStreamHasBytesДоступный опрос - лучшие практики - PullRequest
3 голосов
/ 01 марта 2009

Я сейчас опрашиваю свой CFReadStream для новых данных с CFReadStreamHasBytesAvailable.

(Во-первых, немного предыстории: я делаю свои собственные потоки, и я не хочу / не нужно связываться с вещами runloop, так что вещи с обратными вызовами клиентов здесь на самом деле не применимы).

У меня вопрос: каковы принятые методы опроса?

Документация 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 - это было бы здорово, но я также пытаюсь задержать прогресс загрузки, а также прогресс загрузки, так что блокировка там не будет не поможет ...).

Любое понимание будет высоко ценится - спасибо!

Ответы [ 2 ]

4 голосов
/ 01 марта 2009

Я думаю, что этот вопрос немного парадоксален, потому что вы спрашиваете, каковы лучшие практики для того, чтобы делать что-то, что по своей сути не является лучшей практикой;)

Когда есть совершенно хороший метод для блокировки сетевого ввода-вывода, любой компромисс, который заставляет вас опрашивать, по определению не лучшая практика.

Тем не менее, если вы сделаете опрос, я думаю, что было бы более уместно «запустить цикл выполнения до даты» в вашем потоке, вместо того, чтобы использовать какой-либо метод posix sleep или yield, который вы себе представляете. Помните, что каждый поток получает свой собственный цикл запуска, поэтому, выполняя цикл запуска, вы позволяете Apple использовать свою концепцию лучших практик для блокировки до будущей даты.

Что касается задержки, я не знаю, получите ли вы точный ответ о том, что такое хорошее время. Это компромисс между перегрузкой ЦП циклами опроса и небольшим застреванием в цикле выполнения, когда ввод / вывод готов для чтения из сети.

В идеале, я думаю, что я бы переориентировал ваши усилия на выполнение этой работы с использованием вызовов блокировки ввода / вывода, но если вы придерживаетесь метода опроса и ожидания, не беспокойтесь о конкретном времени задержки. Просто выберите то, что работает и не оказывает негативного влияния на производительность в любом направлении.

(Кроме того, я хотел бы уточнить, что я не слишком религиозен в отношении опроса и блокировки, я только подчеркиваю его ценность, потому что вы, очевидно, ищете более подходящее решение).

1 голос
/ 15 октября 2009

При выполнении ручных соединений на основе 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;
    }
}
...