2011-01-19 3 views
1

Я недавно стал любопытным на iOS. Пожалуйста, укажите мне в том направлении, в котором вы бы взяли, чтобы достичь (если возможно) следующего на современных устройствах iOS ... спасибо!Threading, приоритеты и остатки

Пользователь вводит текст, произнося слово каждые несколько секунд.

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

  • запуска DifficultProcess от главного
  • если DifficultProcess завершает, получить сообщение обратно от него к тому же самому главному
  • отказаться, избавиться от , DifficultProcess, если я хочу, от основного
  • и, наконец, вопрос приоритета: сложный процесс должен иметь гораздо более низкий приоритет, чем основной или пользовательский ввод, я хочу, чтобы у DifficultProcess действительно был приоритет looow; что это возможно?

Какие, по сути, вызовы, которые используются для A, B, C в современных (2011) (конец января) iOS? Меня не интересуют методы папы! И «D» даже возможно в любом случае?

Я предполагаю, что это четыре идеи!

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

(Для тех, кто родился до 1997 года, вы узнаете, что в качестве типичной «спекулятивных обработок» парадигмы.)

Спасибо за указатели для тех, кто может быть потрудился на этом!

ответ

7

Я бы рекомендовал использовать NSOperation и NSOperationQueue для управления фоновыми действиями, которые необходимо отменить произвольно.

Обходные средства NSOperation и NSOperationQueue's -CancelAllOperations - это методы, на которые нужно смотреть.

Чтобы получить сообщения с фона в основной поток, метод dispatch_async-main-thread-que is fine. Вы можете объединить это с протоколом делегата для своей NSOperation, чтобы кодировать сообщения, которые вы хотите отправить назад.

E.g.

@protocol MyOperationDelegate 
- (void) operationStarted:(MyOperation *)operation; 
- (void) makingProgressOnItem:(id)anItem otherInterestingItem:(NSDictionary *)otherItem remainingCount:(NSUInteger)count; 
- (void) operationWillFinish:(MyOperation *)operation; 
@end 

@interface MyOperation 
id <MyOperationDelegate> delegate; 
@end 

@implementation MyOperation 
... 

- (void) cancel 
{ 
    [super cancel]; 

    // Tell the delegate we're about to finish (due to cancellation). 
    dispatch_sync (dispatch_get_main_queue(), ^{ 
     [self.delegate operationWillFinish:self]; 
    }); 
} 

- (void) main 
{ 
    // Check for cancellation 

    if (self.isCancelled) return; 

    // Starting 

    dispatch_sync (dispatch_get_main_queue(), ^{ 
     [self.delegate operationStarted:self]; 
    }); 

    if (self.isCancelled) return; // Another cancel check 


    // Send async progress messages periodically while doing some work 

    while (workNotDone) 
    { 
     // Do some work ... 

     dispatch_async (dispatch_get_main_queue(), ^{ 
      [self.delegate makingProgressOnItem:foo otherInterestingItem:bar remainingCount:baz]; 
     }); 

     if (self.isCancelled) return; 
    } 


    // About to finish 

    if (!self.isCancelled) { 
     dispatch_sync (dispatch_get_main_queue(), ^{ 
      [self.delegate operationWillFinish:self]; 
     }); 
    } 
} 
@end 

КВО не хорошо для interthread связи; наблюдение принимается в потоке, который инициирует изменение значения ключа. Итак, если ваш фоновый поток изменит значение, ваш фоновый поток получит KVO. Наверное, не то, что ты хочешь.

Дедушка -performSelectorOnMainThread: withObject: waitUntilDone: продолжает оставаться прекрасным способом вернуть сообщения в основной поток. Ограничение заключается в том, что ваши сообщения могут обращаться только к одному объектно-ориентированному аргументу. В диспетчере_асинк к основному потоку этого ограничения нет.

Если вы хотите отключить асинхронную (или синхронную) NSNotification от фонового потока к основному потоку, вам нужно использовать -performSelectorOnMainThread.

NSNotification *note = [NSNotification notificationWithName:FinishedABunchOfWorkNotification object:self userInfo:nil]; 
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:note waitUntilDone:YES]; 
+0

Существует несколько подходов: 1) в главном коде потока, сохраняйте явную ссылку на NSOperation и просто вызывайте -cancel на нем. Или 2) сохраните ссылку на свой NSOperationQueue и вызовите на него -cancelAllOperations (который отправит -cancel для всех операций в очереди). –

+0

Apple docs на NSOperation покажет все. Вы можете установить уровень приоритета для операции. –

2

Рискуя квотирования метод папиных (это было вокруг, так как iPhone версия 2) Я использую

- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg

Это легко и несложные до тех пор, как вы помните, что вы должны создать новый autorelease бассейн в метод, который вы передаете как селектор, и слейте его в конце метода. Помимо того, что делайте все, что угодно - ЗА ИСКЛЮЧЕНИЕМ касания UIKit. Он не является потокобезопасным, поэтому любые изменения пользовательского интерфейса должны быть выполнены через

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait 

или триггеры KVO. Замечание о ключевом значении было бы хорошим способом передать фоновый поток в ваш основной поток, чтобы работа была выполнена.

- (void)myBackgroundThreadMethod { 

    NSAutoreleasePool *threadPool = [[NSAutoreleasePool alloc] init]; 

    // my time-consuming processing here 

    [threadPool drain]; 
} 

Для более точного управления потоками вам необходимо посмотреть на NSThread. Threading Programming Guide подробно излагает это - если вы создаете поток через NSThread, тогда у вас есть контроль над запуском потока. Документ рекомендует оставить только один поток и просто разрешить его завершение - но показывает, как его можно закончить. Один из способов: - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait

В документах NSThread также говорится: «Оставьте приоритет в покое».Вы можете установить приоритет потока с

+ (BOOL)setThreadPriority:(double)priority 

, но я никогда не знал, что это будет необходимо, планировщик достаточно умен, чтобы поддерживать пользовательский интерфейс отзывчивость.

+0

GCD идеально подходит для такого рода работ. У меня будет таймер, который будет сбрасываться каждый раз, когда пользователь что-то наберет. Поэтому, когда он срабатывает (при этом пользователь не нажал клавишу, скажем, через 2 секунды), он вызывает dispatch_async, который отключает обработку и отправляет NSNotification в основной поток, когда он сделан. –

+1

Вы уверены, что наблюдатель получает уведомление по основному потоку? Я не тестировал это на iOS, но IIRC, в Mac OS X, наблюдение происходит во всех потоках, которые были изменены. –

+0

Я не был совершенно ясен - вы можете делать свой материал пользовательского интерфейса либо в методе, который вы вызываете, с помощью функции performSelectorOnMainThread, либо вы можете запускать его через KVO из этого метода в основном потоке. Я думаю, что вы правы в уведомлении, происходящем на меняющемся потоке. –

3

Я бы предложил использовать dispatch_async для глобальной очереди с низким приоритетом (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_LOW, 0)).

Отмена сложнее, хотя. Нет никакого хорошего общего механизма для отмены фоновой работы, о которой я знаю, помимо «разбиения» на нее и проверки флага каждый фрагмент.

Чтобы вернуть сообщения обратно, просто dispatch_async вернитесь в основную очередь. Если вы правильно подойдете, вы можете думать о dispatch_async как «отправить сообщение» в модели актера.

(изменить), если вам нужна сериализация материала в фоновом режиме, сделайте приватную очередь и установите ее цель на глобальный низкоприоритетный, iirc.

+0

Итак, вы не можете прервать функцию, запущенную в фоновом потоке.Что вы можете сделать, так это разделить работу на куски: __block dispatch_block_t chunk; chunk =^{doChunkOfBackgroundWork(); dispatch_async (myQueue, chunk); }; dispatch_async (myQueue, chunk); Затем вы можете вставлять материал в фоновый поток между кусками работы, просто отправляя_ [a] sync() в одну очередь. Например, он может установить переменную, которую затем обрабатывает рабочий кусок. –