3

ドキュメントに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

(完全に機能することもありますが、このように奇妙なこともあります。)

4

1 に答える 1

3

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);
});

私の 8core MacBook Pro では:

4
2
0
6
3
1
5
8
9
7
于 2011-07-07T08:48:51.197 に答える