为什么并发队列上的屏障行为很奇怪? [英] Why weird behavior of barrier on concurrent queue?

查看:79
本文介绍了为什么并发队列上的屏障行为很奇怪?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试验证dispatch_barrier_async的功能,并使用兴趣点"检查发生了什么.

I am trying to verify the functionality of dispatch_barrier_async, and use "points of interest" to check what happened.

代码如下:

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

for (int index = 0; index < 3; ++index) {
    kdebug_signpost_start(0, 0, 0, 0, 0);
    dispatch_sync(_syncQueue, ^{
        NSLog(@"sync@@@@@@ >>>>    %d ",index);
        sleep(1);
        NSLog(@"sync@@@@@@ <<<<    %d ",index);

    });
    kdebug_signpost_end(0, 0, 0, 0, 0);
}

for (int index = 3; index < 6; ++index) {
    kdebug_signpost_start(1, 0, 0, 0, 1);
    dispatch_barrier_async(_syncQueue, ^{
        NSLog(@"sync===== >>>>    %d ",index);
        sleep(1);
        NSLog(@"sync===== <<<<    %d ",index);

    });
    kdebug_signpost_end(1, 0, 0, 0, 1);
}

for (int index = 6; index < 9; ++index) {
    kdebug_signpost_start(2, 0, 0, 0, 2);
    dispatch_sync(_syncQueue, ^{
        NSLog(@"sync***** >>>>    %d ",index);
        sleep(1);
        NSLog(@"sync***** <<<<    %d ",index);
    });
    kdebug_signpost_end(2, 0, 0, 0, 2);
}

我希望

  • dispatch_barrier_async(第二个循环)分派的所有任务应在正在执行的任务完成后才能工作;和

  • all tasks dispatched by dispatch_barrier_async (the 2nd loop) should work after the tasks which is executing finishes; and

一旦dispatch_barrier_async分派的任务开始工作,就无法运行同一队列中的其他任务.

once the tasks dispatched by dispatch_barrier_async start to work, no other task in same queue can be run.

但是结果如下:

sync@@@@@@ >>>>    0
sync@@@@@@ <<<<    0
sync@@@@@@ >>>>    1
sync@@@@@@ <<<<    1
sync@@@@@@ >>>>    2
sync@@@@@@ <<<<    2
sync***** >>>>    6
sync===== >>>>    3
sync===== >>>>    4
sync===== >>>>    5
sync***** <<<<    6
sync***** >>>>    7
sync===== <<<<    3
sync===== <<<<    4
sync===== <<<<    5
sync***** <<<<    7
sync***** >>>>    8

我的困惑如下:

  1. 由于任务6在任务3之前已经启动,并且任务3是dispatch_barrier_async调度的任务,为什么任务3不等待任务6完成然后启动?这违反了我的期望a);

  1. since task 6 is already started before task 3, and task 3 is dispatch_barrier_async dispatched task, why taks 3 does not wait task 6 finish and then start ? which violates my expectation a);

dispatch_barrier_async分派的任务5开始工作后,在任务5完成之前又启动了另一个任务7,这违反了我的预期b)

after dispatch_barrier_async dispatched task 5 start to work, another task 7 is started before task 5 finished, which violates my expectation b)

我使用兴趣点"工具对其进行调试,但是似乎第二个循环没有显示在由仪器生成的图形上.这是我第一次使用此工具,有人可以帮忙指出这里出什么问题吗?

I use "points of interest" tool to debug it, but seems, the 2nd loop does not shows on the graphic generated by intrument. it's my first time to use this tool, Could someone help to point out what's the issue here ?

[04/02] 谢谢罗伯.遵循您的建议,这里有一些更新.

[04/02] Thanks Rob. Here are some update after following your suggestion.

Update1: 将第二个循环的队列创建方法更改为dispatch_queue_create之后,它可以很好地工作. dispatch_barrier_async调度的任务将等待任务完成.并且它们之后的任务将开始使用(由dispatch_barrier_async调度的任务)完成.

Update1: After change the queue creation method of 2nd loop to dispatch_queue_create, it works well. the tasks dispatched by dispatch_barrier_async will wait for the tasks before them to finish. and the tasks after them will start util they(tasks dispatched by dispatch_barrier_async) finished.

update2: 然后,我使用了您提供的代码,并将第二个循环的队列创建更改回dispatch_async,它们也按预期工作. 但是,兴趣点"图形存在一些问题.第二循环中的某些任务无法正确显示.

update2: Then I used the code you provided, and change the queue creation of 2nd loop back to dispatch_async, they also works as expection. But, the "points of interest" graphic has some problem. some task in 2nd loop cannot be displayed correctly.

  1. Xcode指示(0x4 0x0 0x0 0x1)和(0x5 0x0 0x0 0x1)有一些错误,因为由于无法确定起点,因此该间隔无法记录为间隔."

  1. Xcode indicate some error for (0x4 0x0 0x0 0x1) and (0x5 0x0 0x0 0x1), as "The interval can not be logged as an interval because the start could not be determined."

(0x3 0x0 0x0 0x1)未列出,但(0x5 0x0 0x0 0x1)被列出了两次.在图形上只显示一个(0x5 0x0 0x0 0x1)

the (0x3 0x0 0x0 0x1) is not listed, but (0x5 0x0 0x0 0x1) is listed twice. and only one (0x5 0x0 0x0 0x1) displayed on the graphic

在兴趣点"工具中我从未看到指示器Ⓢ.

I never see an indicator Ⓢ in the "points of interest" tool.

这将产生以下消息:

00:00.000.000 2ndLoop(KDebug Interval Signpost)以test.app(pid:64903,tid:0x1619027)结尾,参数为(0x4 0x0 0x0 0x1).由于无法确定起点,因此无法将间隔记录为间隔. __kdebug_trace64←(其他8帧)

00:00.000.000 2ndLoop (KDebug Interval Signpost) ended by test.app (pid: 64903, tid: 0x1619027), with arguments: (0x4 0x0 0x0 0x1). The interval can not be logged as an interval because the start could not be determined. __kdebug_trace64 ← (8 other frames)

00:00.000.000 2ndLoop(KDebug Interval Signpost)以test.app(pid:64903,tid:0x1619028)结尾,参数为(0x5 0x0 0x0 0x1).由于无法确定起点,因此无法将间隔记录为间隔. __kdebug_trace64←(其他8帧)

00:00.000.000 2ndLoop (KDebug Interval Signpost) ended by test.app (pid: 64903, tid: 0x1619028), with arguments: (0x5 0x0 0x0 0x1). The interval can not be logged as an interval because the start could not be determined. __kdebug_trace64 ← (8 other frames)

00:04.134.364 2ndLoop(KDebug Interval Signpost),由test.app(pid:64903,tid:0x1619028)启动,参数为:(0x5 0x0 0x0 0x1),在1.00 s后由test.app(pid)结束:64903,tid:0x1619026),参数:(0x3 0x0 0x0 0x1)__kdebug_trace64←(另外9个帧)

00:04.134.364 2ndLoop (KDebug Interval Signpost), started by test.app (pid: 64903, tid: 0x1619028), with arguments: (0x5 0x0 0x0 0x1), ended 1.00 s later by test.app (pid: 64903, tid: 0x1619026), with arguments: (0x3 0x0 0x0 0x1) __kdebug_trace64 ← (9 other frames)

推荐答案

有两个独立的问题:

  1. 您不能对全局队列使用障碍.屏障表示在此已调度任务正在运行时,请勿在此队列上运行任何内容",因此,当您尝试在全局队列上执行此操作时,操作系统显然不喜欢它.正如文档所述,它将仅遵守自定义方面的障碍并发队列,而不是全局队列:

  1. You cannot use barriers with global queues. A barrier says "don't run anything on this queue while this dispatched task is running", so the OS obviously doesn't like it when you try to do that on a global queue. As the documentation says, it will only honor barriers on custom concurrent queues, not global queues:

您指定的队列应该是使用 dispatch_async 函数

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function

创建您自己的并发队列,它应能按预期工作.

Create your own concurrent queue, and it should work as expected.

dispatch_queue_t queue = dispatch_queue_create("com.domain.app.test", DISPATCH_QUEUE_CONCURRENT);

屈服:

您的兴趣点"图显示的是调度过程(即,在主线程上占用了多少时间),而不是任务本身的运行.因此,在您的第二个循环迭代中,它们的异步调度分别只花费了25.54、5.17和4.72μs(微秒).这就是为什么您在兴趣点"工具中看不到图形化的第二次迭代的原因.它们发生得太快而无法以图形表示. (顺便说一句,这是一件非常好的事情,这就是为什么我们从不同步调度慢任务;我们从不希望阻塞主线程的原因.)

Your "Points of Interest" graph is showing the dispatching process (i.e. how much time is being taken up on the main thread), not the running of the tasks, themselves. So in your 2nd loop iterations, the asynchronous dispatching of them only took 25.54, 5.17, and 4.72 μs (microseconds) each. That's why you don't see your second iterations graphically in the "Points of Interest" tool. They're happening too quickly to be graphically represented. (By the way, this is a very good thing, and is why we never dispatch slow tasks synchronously; we never want to block the main thread.)

通常,在诊断GCD行为时,我发现它对兴趣点"区域表示任务的运行而不是任务的分配更为有用.

Generally, when diagnosing GCD behavior, I find it more useful to the "points of interest" regions to represent the running of the tasks, not the dispatching of the tasks.

要实现此目的,将os_signpost语句(以前为kdebug)放在这些块中可能会更有用,从而产生上图.因此:

To achieve this, it's probably more useful to put the os_signpost statements (formerly kdebug) inside the blocks, yielding the above diagram. Thus:

dispatch_queue_t queue = dispatch_queue_create("com.domain.app.test", DISPATCH_QUEUE_CONCURRENT);

os_log_t log = os_log_create("ViewController", OS_LOG_CATEGORY_POINTS_OF_INTEREST);

for (int index = 0; index < 3; ++index) {
    os_signpost_event_emit(log, OS_SIGNPOST_ID_EXCLUSIVE, "first loop", "dispatching iteration %d", index);
    dispatch_sync(queue, ^{
        os_signpost_id_t identifier = os_signpost_id_generate(log);
        os_signpost_interval_begin(log, identifier, "first loop", "Iteration %d", index);
        sleep(1);
        os_signpost_interval_end(log, identifier, "first loop", "Iteration %d", index);
    });
}

for (int index = 3; index < 6; ++index) {
    os_signpost_event_emit(log, OS_SIGNPOST_ID_EXCLUSIVE, "second loop", "dispatching iteration %d", index);
    dispatch_barrier_async(queue, ^{
        os_signpost_id_t identifier = os_signpost_id_generate(log);
        os_signpost_interval_begin(log, identifier, "second loop", "Iteration %d", index);
        sleep(1);
        os_signpost_interval_end(log, identifier, "second loop", "Iteration %d", index);
    });
}

for (int index = 6; index < 9; ++index) {
    os_signpost_event_emit(log, OS_SIGNPOST_ID_EXCLUSIVE, "third loop", "dispatching iteration %d", index);
    dispatch_sync(queue, ^{
        os_signpost_id_t identifier = os_signpost_id_generate(log);
        os_signpost_interval_begin(log, identifier, "third loop", "Iteration %d", index);
        sleep(1);
        os_signpost_interval_end(log, identifier, "third loop", "Iteration %d", index);
    });
}


在修改后的问题中,您问:


In your revised question, you ask:

然后,我使用了您提供的代码,并将第二个循环的队列创建更改回dispatch_async,它们也按预期工作.但是,兴趣点"图形存在一些问题.第二循环中的某些任务无法正确显示.

Then I used the code you provided, and change the queue creation of 2nd loop back to dispatch_async, they also works as expection. But, the "points of interest" graphic has some problem. some task in 2nd loop cannot be displayed correctly.

  1. Xcode指示(0x4 0x0 0x0 0x1)和(0x5 0x0 0x0 0x1)有一些错误,因为由于无法确定起点,因此该间隔无法记录为间隔."

  1. Xcode indicate some error for (0x4 0x0 0x0 0x1) and (0x5 0x0 0x0 0x1), as "The interval can not be logged as an interval because the start could not be determined."

(0x3 0x0 0x0 0x1)未列出,但(0x5 0x0 0x0 0x1)被列出了两次.在图形上只显示一个(0x5 0x0 0x0 0x1)

the (0x3 0x0 0x0 0x1) is not listed, but (0x5 0x0 0x0 0x1) is listed twice. and only one (0x5 0x0 0x0 0x1) displayed on the graphic

兴趣点"的窍门是,在执行GCD时,它将重用工作线程,因此在如何匹配开始和结束kdebug调用(这是我们以前使用过的方法)方面造成了混乱到os_signpost API).

The trick with "Points of Interest" is that when doing GCD, it will reuse the worker threads, so it presents confusion as to how to match the start and end kdebug calls (which is what we used to use prior to os_signpost API).

如果使用旧的kdebug API,则可以将循环号作为代码"参数传递给kdebug调用,并将迭代号作为此后的第一个参数.然后,您可以转到乐器中的录音选项",并通过以下两种方式告诉它与开始/结束调用相匹配:(a)代码;和(b)代码后的第一个参数:

If you are using the old kdebug API, you can pass the loop number as the "code" parameter to the kdebug calls and the iteration number as the first parameter after that. You can then go to "Recording Options" in Instruments and tell it to match start/end calls by both (a) the code; and (b) the first parameter after the code:

在更新的os_signpost API中,您可以如上所述调用os_signpost_id_generate来生成一个标识符,兴趣点将使用该标识符为您匹配路标调用.

In the newer os_signpost API, you can call os_signpost_id_generate, as shown above, to generate an identifier, and points of interest will use that to match up the signpost calls for you.

您继续问:

  1. 我从没在兴趣点"工具中看到指标

好吧,如果要查看这些内容,则必须通过os_signpost_event_emit(或os_log)将它们发布到兴趣点日志中,如上面的代码片段所示.

Well, if you want to see those, you have to post them via os_signpost_event_emit (or os_log) to the points of interest log, as shown in the code snippet above.

再次注意,Ⓢ路标指示我何时从主线程进行调度调用,彩色区域指示该并发队列上任务的时间安排.

Note, again, the Ⓢ signposts indicate when I did the dispatch calls from from the main thread, and the colored regions indicate the timing of the tasks on that concurrent queue.

我建议您观看WWDC 2016视频深入了解系统跟踪有关如何使用兴趣点"工具的详细信息.

I'd suggest you watch WWDC 2016 video System Trace in Depth for detailed information about how to use "Points of Interest" tool.

这已针对macOS 10.14和iOS 12.0更新.对于使用kdebug API的早期OS版本,请参见此答案的先前版本.

This has been updated for macOS 10.14 and iOS 12.0. For earlier OS versions, using the kdebug API, see previous revision of this answer.

这篇关于为什么并发队列上的屏障行为很奇怪?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆