Разве dispatch_semaphore_wait не является FIFO? - PullRequest
3 голосов
/ 06 июля 2011

Документация для dispatch_semaphore_wait говорит, что он "ожидает в FIFO-порядке сигнала".Но, похоже, в этом примере - кто-то может объяснить, пожалуйста?

Пример:

#include <dispatch/dispatch.h>
#include <stdio.h>

dispatch_queue_t q1, q2;
dispatch_semaphore_t sem;
int g_call;

void do_work(void)
{
    int s = 0;
    int i;
    for (i = 0; i < 100000000; ++i)
        ++s;
}

void f1(int call)
{
__block int waited = 0;
    dispatch_async(q1, ^{
        while (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC/1000)))
            waited = 1;
        printf("1:%d %s\n", call, waited ? "waited" : "");
        do_work();
        dispatch_semaphore_signal(sem);
    });
}

void f2(int call)
{
    __block int waited = 0;
    dispatch_async(q2, ^{
        while (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC/1000)))
            waited = 1;
        printf("\t\t2:%d %s\n", call, waited ? "waited" : "");
        do_work();
        dispatch_semaphore_signal(sem);
    });
}

int main(int argc, char **argv)
{
    q1 = dispatch_queue_create(NULL, NULL);
    q2 = dispatch_queue_create(NULL, NULL);
    sem = dispatch_semaphore_create(1);
    g_call = 0;

    dispatch_queue_t q_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, q_global);
    const uint64_t DELAY = 10;
    dispatch_source_set_event_handler(timer, ^{
        f1(g_call);
        f2(g_call);
        ++g_call;
        dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, DELAY), 0, 0);
    });
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, DELAY), 0, 0);
    dispatch_resume(timer);

    sleep(3);
}

Ожидаемый результат:

1:0
        2:0
1:1
        2:1
1:2
        2:2
...

Фактический результат (один пример):

1:0
1:1
...
1:14
        2:0 waited
        2:1
        ...

Редактировать : Фактический вывод, если вместо последовательных очередей q1 и q2 установлены в глобальную очередь:

1:0 
        2:8 waited
1:3 waited
1:4 waited
        2:3 waited
1:6 waited
1:9 waited
        2:9 waited
        2:21 
1:28 waited

(иногда это работает отлично, но иногда это странно, как это.)

1 Ответ

3 голосов
/ 07 июля 2011

dispatch_queue_create создает последовательную очередь, а затем последовательная очередь создает поток pthread (я не уверен ...).

А dispatch_semaphore_wait использует спин-блокировки для получения семафора для повышения производительности. Это означает, что это не точка переключения контекста, как pthread_mutex_lock. Он не так часто переключает контекст.

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

q1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
q2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

РЕДАКТИРОВАНИЕ:

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

Другой простой пример,

dispatch_queue_t queue =
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
    printf("%zu\n", index);
});

На моем 8-ядерном MacBook Pro:

4
2
0
6
3
1
5
8
9
7
...