#objective-c #macos #dispatch-async
#objective-c #macos #отправка-асинхронная
Вопрос:
-
возникает ли какая-либо проблема в следующем коде при тайм-ауте (без регистра ARC)?
-
как насчет того, что dispatch_semaphore_signal вызывается после dispatch_release(семафор)? Я знаю, что объект отправки асинхронно освобождается после освобождения всех ссылок на него (счетчик ссылок становится равным нулю), поэтому ссылка на семафор не равна 0 в следующем коде?
-
необходимо ли добавлять __block для семафора?
dispatch_time_t timeout = DISPATCH_TIME_FOREVER; if (waitTime > 0) { timeout = dispatch_time(DISPATCH_TIME_NOW, waitTime * NSEC_PER_SEC); } dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_barrier_async(_dispatchQueue, ^{ dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, timeout); dispatch_release(semaphore);
большое спасибо!
Комментарии:
1. Я думаю , вам нужно сообщить об этом, если вы достигнете тайм-аута, иначе релиз завершится неудачно.
2. @skaak, спасибо. вы имеете в виду, что ссылка на seamphore в этом случае не будет равна 0, верно? нужно ли сначала отменить блок, а затем сигнализировать об этом?
3. Нет, не ссылка… сам счетчик семафоров… Я думаю, что семафор будет жаловаться, если вы его отпустите, а ожидание и сигналы не будут сбалансированы. Поскольку у вас есть сигнал, который никогда не запускался, я думаю, вы могли бы просто добавить еще один сигнал перед выпуском, чтобы убедиться, что релиз будет работать. Я не думаю, что это имеет значение, если вы сигнализируете больше, чем ожидаете, например, если он не истекает, но срабатывает дважды, я думаю, что релиз будет в порядке.
4. У меня болит голова! Если вы освободите себя, вам также нужно позаботиться о блоке, поэтому я думаю, что да, вам нужно его отменить.
5. Проблема в том, что после истечения времени ожидания семафор может по-прежнему сигнализировать в любое время, пока вы заботитесь об администраторе здесь. Это почти так, как если бы вам нужна была некоторая синхронизация для семафора и его освобождения.
Ответ №1:
На основе комментариев.
Я думаю, вам нужно что-то вроде этого. done
Переменная должна быть синхронизирована / атомарна, и в коде есть два места, которые необходимо синхронизировать с некоторой общей блокировкой. На самом деле, поскольку вы получаете доступ только done
к этим частям, синхронизировать их не нужно.
done = NO
dispatch_barrier ...
stuff
sync {
if(!done)
signal
done = YES;
}
wait with timeout
cancel block
sync {
if( ! done )
signal
done = YES
}
// You can even test the logic as below
if DEBUG
{
assert done == YES
}
release
Я не уверен, какое влияние окажет блок отмены в среде, отличной от ARC, но думаю, что все будет хорошо.
Это сбалансирует сигналы и ожидания и заставит релиз работать, а также не позволит вам сигнализировать о выпущенном семафоре.
Вот примерная реализация в Objective-C
__block BOOL done = NO;
NSObject * lock = NSObject.new;
dispatch_semaphore_t s = dispatch_semaphore_create ( 0 );
dispatch_queue_t queue = dispatch_queue_create( "bak",
dispatch_queue_attr_make_with_qos_class( DISPATCH_QUEUE_CONCURRENT,
QOS_CLASS_DEFAULT,
DISPATCH_QUEUE_PRIORITY_DEFAULT ) );
dispatch_block_t block = dispatch_block_create ( DISPATCH_BLOCK_BARRIER, ^ {
// Change this time to determine which one fires first
[NSThread sleepForTimeInterval:1];
@synchronized ( lock ) {
if ( ! done )
{
done = YES;
dispatch_semaphore_signal ( s );
NSLog ( @"Inside fired" );
}
}
} );
// Start the block
dispatch_async ( queue, block );
// ... or change time here to determine which one fires first
dispatch_semaphore_wait ( s, dispatch_time ( DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC ) );
@synchronized ( lock ) {
if ( ! done )
{
done = YES;
dispatch_semaphore_signal ( s );
NSLog ( @"Outside fired" );
}
}
// Done, release stuff *only* if not ARC
dispatch_release ( s );
dispatch_release ( queue );
dispatch_release ( block );
lock.release;
Комментарии:
1. есть ли какой-либо способ использовать std::atomic в блоке?
2. Функция dispatch_semaphore_wait() уменьшает значение семафора. Если результирующее значение меньше нуля, он ожидает сигнала от потока, который увеличивает семафор, вызывая dispatch_semaphore_signal() перед возвратом. Параметр timeout может быть создан с помощью функций dispatch_time(3) или dispatch_walltime(3). Если тайм-аут достигнут без получения сигнала, семафор повторно увеличивается до возврата функции, так что это означает, что нам не нужен dispatch_semaphore_signal в случае тайм-аута?
3. Ах, я понимаю, что вы имеете в виду. Да, я думаю, вы правы, вам не нужно сигнализировать семафору, чтобы освободить его. Тем не менее, вам все еще нужна защита, иначе вы можете подать сигнал внутри после освобождения семафора. Если отмена блока гарантирует, что выполнение немедленно прекратится, это также может исчезнуть.
4. Я тестировал это в программе с отключенной ARC — да, вы можете выпустить без балансировочного сигнала. Из нескольких тестов мой код кажется чрезвычайно консервативным, вам может сойти с рук намного меньше, но, конечно, то, что вы сокращаете, также зависит от других вещей, например, когда вы отпускаете и как вы отменяете блок, если время ожидания достигнуто.