У меня есть код, в котором я использую dispatch_semaphore_t, чтобы сигнализировать о завершении операции. Когда семафор является переменной-членом, кажется, что он ведет себя неправильно. Я покажу пример кода, который работает, и пример, который, кажется, не работает:
@implementation someClass
{
dispatch_semaphore_t memberSem;
dispatch_semaphore_t* semPtr;
NSThread* worker;
BOOL taskDone;
}
- (id)init
{
// Set up the worker thread and launch it - not shown here.
memberSem= dispatch_semaphore_create(0);
semPtr= NULL;
taskDone= FALSE;
}
- (void)dealloc
{
// Clean up the worker thread as needed - not shown here.
if((NULL != semPtr) && (NULL != *semPtr))
disptatch_release(*semPtr);
dispatch_release(memberSem);
}
- (void)doSomethingArduous
{
while([self notDone]) // Does something like check a limit.
[self doIt]; // Does something like process data and increment a counter.
taskDone= TRUE; // I know this should be protected, but keeping the example simple for now.
if((NULL != semPtr) && (NULL != *semPtr))
dispatch_semaphore_signal(*semPtr); // I will put a breakpoint here, call it "SIGNAL"
}
- (BOOL)getSomethingDoneUseLocalSemaphore
{
taskDone= FALSE; // I know this should be protected, but keeping the example simple for now.
dispatch_semaphore_t localSem= dispatch_semaphore_create(0);
semPtr= &localSem;
[self performSelector:doSomethingArduous onThread:worker withObject:nil waitUntilDone:NO];
dispatch_time_t timeUp= dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(2.5 * NSEC_PER_SEC));
dispatch_semaphore_wait(localSem, timeUp);
semPtr= NULL;
dispatch_release(localSem);
// I know I could just return taskDone. The example is this way to show what the problem is.
if(taskDone) // Again with thread safety.
return TRUE;
return FALSE;
}
- (BOOL)getSomethingDoneUseMemberSemaphore
{
taskDone= FALSE; // I know this should be protected, but keeping the example simple for now.
semPtr= &memberSem; // I will put a breakpoint here, call it "START"
[self performSelector:doSomethingArduous onThread:worker withObject:nil waitUntilDone:NO];
dispatch_time_t timeUp= dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(2.5 * NSEC_PER_SEC));
dispatch_semaphore_wait(memberSem, timeUp);
semPtr= NULL;
// I know I could just return taskDone. The example is this way to show what the problem is.
if(taskDone) // Again with thread safety.
return TRUE; // I will put a breakpoint here, call it "TASK_DONE"
return FALSE; // I will put a breakpoint here, call it "TASK_NOT_DONE"
}
- (void)hereIsWhereWeBringItTogether
{
BOOL gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
gotItDoneLocal= [self getSomethingDoneUseLocalSemaphore]; // Will return TRUE.
BOOL gotItDoneMember= [self getSomethingDoneUseMemberSemaphore]; // Will return TRUE. I will put a breakpoint here, call it "RUN_TEST"
gotItDoneMember= [self getSomethingDoneUseMemberSemaphore]; // Will return FALSE.
}
Итак, учитывая этот код и результаты, которые я получаю/получаю, я ставлю точки останова, как описано в моем реальном коде: одну в основной функции, одну для запуска в рабочей функции, одну, где сигнализируется семафор-член, и две после ждать.
То, что я обнаружил, было в случае, когда я использую семафор члена, в первом раунде я останавливаюсь в точке останова «RUN_TEST», запускаю и нажимаю точку останова «START», запускаю, затем нажимаю точку останова «SIGNAL», запускаю, затем нажимаю точку останова «TASK_DONE» - все как положено.
Когда я продолжаю работать, я нажимаю точку останова «START», запускаю, затем нажимаю точку останова «TASK_NOT_DONE», запускаю, затем нажимаю точку останова «SIGNAL».
То есть, когда я запускаю последовательность, используя семафор, который является членом, и делаю то, что выглядит как правильный сигнал/ожидание, во второй раз, когда я пытаюсь ждать на этом семафоре, я, кажется, пролетаю мимо, и он получает сигнал после того, как я вышел из ждать.
Кажется, я либо не управляю правом подсчета (пары сигнал/ожидание), либо этот семафор-член не вернется в несигнальное состояние.
Мне кажется, что здесь мне не хватает чего-то фундаментального. Мы будем признательны за любой вклад.
РЕДАКТИРОВАТЬ: В конечном счете, то, что мне, казалось, не хватало, было связано с тем, что мой фактический код был немного сложнее. Вместо чистого возврата из сложной задачи задействовано несколько потоков и пост-уведомление. Я заменил postNotification на код в обработчике уведомлений — он устанавливает флаг и сигнализирует семафору. Таким образом устраняется любая задержка, которая могла быть вызвана обработчиком уведомлений.