【问题标题】:DispatchQueue Sync ConcurrentDispatchQueue 同步并发
【发布时间】:2017-06-08 07:24:32
【问题描述】:

好的,所以我已经经历了很多问题和答案,并且我知道对它的理解,但是当我尝试一些代码时,我得到了一些不支持这些理论的结果。

到目前为止我所理解的:

  1. 同步:一旦块内的所有任务都执行完毕,控制权就会返回。
  2. 异步:在块被击中后控制将立即返回。

这是看起来不错的部分。 现在是棘手的部分

  1. 串行:任务将在块内以串行方式执行。 Q1:这适用于块内的任务吗?因为它已经在所有情况下发生。同样对于同步串行队列,在不同的块中添加任务并不重要,因为一旦第一个任务完成,控制权就会返回。

例如-

 let syncQ = DispatchQueue(label:"xyz") // by default it is serial
  syncQ.sync{
      for _ in 0...10{
          print("ABC")
      }
  }

  syncQ.sync{
      for _ in 0...10{
          print("XYZ")
      }
  }

预期输出:ABC * 10 , XYZ * 10 这很好。

现在当我引入并发串行 Q 时,输出是相同的。所以我的问题是并发队列说任务将在同一时间或同时完成,它没有发生。

例如 -

let syncConc = DispatchQueue(label:"con",attributes:.concurrent)
syncConc.sync{
       for _ in 0...10{
          print("XYZ")
       }

       for _ in 0...10{
          print("ABC")
       }

}


  syncConc.sync{
       for _ in 0...10{
          print("HHH")
       }

       for _ in 0...10{
          print("XXX")
       }

}

输出:XYZ *10 ,ABC*10, HHH*10, XXX*10

所以看起来同步并发队列,就像串行队列一样,并且进行并发操作的唯一方法是,如果我们在操作之间抛出一个异步队列。 所以从这里我无法理解,并发类型的串行队列的目的是什么。

如果有人可以提供编码示例,将不胜感激,因为我已经知道它的理论和工作原理。 非常感谢。

【问题讨论】:

  • 并发类型的串行队列的目的是什么???你是如何推断串行队列可能是并发的?队列是串行或并发的。这很容易
  • 不需要弄清楚吗?因为我们可以选择使串行队列并发。我的问题是,在一个串行队列中添加多个任务应该是并发的。在文档中它说这应该发生,在所有解释性教程中它也再次这样说,但是当我尝试做同样的事情时它不会发生。也许这是我想念的东西
  • 信息从哪里来?如果分派给它的所有任务都是串行执行的,则队列是串行的。如果分派给它的所有任务都同时执行,则队列是并发的。请再次检查文档或查看iosbrain.com/blog/2017/02/06/…
  • 对不起,我的意思是:同步 - 并发队列,这就是我的问题所在。我确实得到队列的类型:串行和并发。但是当我创建一个并发和同步的队列时,它的行为不像它所说的那样。希望我能消除混乱
  • 确实如此。同步意味着,您将任务分派到队列的线程被阻塞,直到任务完成执行(在某个线程上)。您可以同时从不同的线程分派更多同步任务。所有任务将同时执行。在您的示例中,您从 main 分派,main 在任务执行时被阻塞,然后您分派其他任务 ...

标签: ios swift grand-central-dispatch


【解决方案1】:

问题在于您混淆了队列类型和执行模型。

有串行和并发队列,您可以同步或异步向两种类型分派任务。

队列可以是:

  • 串行 -> 一次只执行一项任务
  • 并发 -> 可以同时运行多个任务

我们可以将任务提交到队列:

  • 同步 -> 调用者需要等待该任务返回
  • 异步 -> 调用者代码无需等待任务完成即可重新获得控制权

总结一下:

  • 队列的串行或并发类型决定了它是否可以同时执行一项或多项任务
  • 同步或异步调度任务确定调用者的代码何时重新获得控制权

【讨论】:

  • 谢谢,@michaelxing。不过,我想知道为什么它被否决了。
【解决方案2】:

实际上,在您的代码中,当您在并发队列(第二个 sn-p)中执行任务时,您正在通过同步块分派任务,因此这将根据同步行为阻塞当前线程。

“同步:一旦块内的所有任务都执行完毕,控制权就会返回。”

   let syncConc = DispatchQueue(label:"con",attributes:.concurrent)

   syncConc.sync{
      for _ in 0...10{
        print("XYZ")
      }

      for _ in 0...10{
        print("ABC")
      }
   }


   syncConc.sync{
      for _ in 0...10{
        print("HHH")
      }

      for _ in 0...10{
        print("XXX")
      }
   }

所以,在这种情况下,首先 suncConc 队列将调度第一个同步块,现在由于它是阻塞调用,下一个任务不会立即调度,它将被调度,一旦第一个任务完成,然后它将在 suncConc 队列中分派,并通过阻塞调用再次执行。

现在,让我来回答您的问题

“现在当我引入并发串行 Q 时,输出是相同的。所以我的问题是并发队列说任务将在同一时间或同时完成,它没有发生。”

是的,同步操作也可以同时执行,但只有在不阻塞当前线程的情况下立即分派两个调用时才有可能。检查下面的sn-p,两个同步任务是从不同的队列中调度的,因此会并发执行。

    let syncConc = DispatchQueue(label:"con",attributes:.concurrent)


    DispatchQueue.global(qos: .utility).async {
        syncConc.sync{
            for _ in 0...10{
                print("XYZ - \(Thread.current)")
            }
            for _ in 0...10{
                print("ABC - \(Thread.current)")
            }
        }
    }


    DispatchQueue.global(qos: .userInitiated).async {
        syncConc.sync{
            for _ in 0...10{
                print("HHH - \(Thread.current)")
            }

            for _ in 0...10{
                print("XXX - \(Thread.current)")
            }
        }
    }

执行代码并查看所有理论的神奇之处:)

【讨论】:

  • 我有点困惑,为什么这会起作用?在全局队列中调度并发同步任务,那么为什么我们首先需要并发同步任务呢?
  • 没有“并发同步任务”之类的东西。队列可以是并发的,任务可以同步或异步调度到队列中。
【解决方案3】:

为了能够了解串行/并发队列之间以及同步/异步方法之间的区别以及它们是如何分派到队列中的,请尝试在 Playground 上使用下一个 sn-p。

import PlaygroundSupport
import Dispatch
PlaygroundPage.current.needsIndefiniteExecution = true

let q = DispatchQueue(label: "concurrect queue", qos: .background, attributes: .concurrent)

func runner0(queue: DispatchQueue) {
    for _ in 1..<10 {
        let result = queue.sync { ()->Int in
            usleep(100)
            return 0
        }
        DispatchQueue.main.async {
            print(result, "-")
        }
    }
}

func runner1(queue: DispatchQueue) {
    for _ in 1..<10 {
        let result = queue.sync { ()->Int in
            usleep(100)
            return 1
        }
        DispatchQueue.main.async {
            print("-", result)
        }
    }
}

let arr = [runner0, runner1]

DispatchQueue.concurrentPerform(iterations: 2) { (i) in
    arr[i](q)
}

我们有一个并发队列,其中两个不同的运行器同步调度任务。如您所见,我们并发队列中的所有任务都是并发执行的,然后将结果串行(异步)打印到串行队列上。

我希望这可以帮助您了解它是如何工作的。

我的游乐场打印

- 1
0 -
- 1
0 -
0 -
- 1
0 -
- 1
0 -
- 1
0 -
- 1
0 -
0 -
- 1
0 -
- 1
- 1

让我们通过改变其定义来使队列成为串行

let q = DispatchQueue(label: "serial queue")

并比较我们得到的结果

0 -
- 1
0 -
- 1
0 -
- 1
0 -
- 1
0 -
- 1
0 -
- 1
0 -
- 1
0 -
- 1
0 -
- 1

【讨论】:

    猜你喜欢
    • 2020-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-07
    • 2022-12-10
    • 2019-07-05
    • 1970-01-01
    相关资源
    最近更新 更多