DispatchQueue.global().sync vs 主线程之间的区别是什么?

huangapple go评论65阅读模式
英文:

What is the difference between DispatchQueue.global().sync vs main thread

问题

代码部分不需要翻译,以下是翻译好的部分:

下面这两段代码在输出结果相同的情况下有何区别?

第一段代码:

print("1")
print("k")
print("2")
print("3")
print("4")

第二段代码:

print("1")
DispatchQueue.global().sync {
    print("k")
}
print("2")
print("3")
print("4")

我想了解sync如何与global()队列一起工作。

英文:

what is the difference between below 2 piece of code while output is same for both

print("1")
print("k")
print("2")
print("3")
print("4")
print("1")
DispatchQueue.global().sync {
    print("k")
}
print("2")
print("3")
print("4")

I want to understand how sync works with global() queues.

答案1

得分: 1

首先,我们需要了解什么是DispatchQueue。简而言之,DispatchQueue是一个管理任务在我们应用程序的主线程或后台线程上串行或并发执行的对象(详细信息请参见)。我们可以创建与主线程(MAIN)关联的dispatchQueue,或者获取具有指定服务质量类别的全局(GLOBAL)系统队列。

简而言之,这两者(MAIN和GLOBAL)都是dispatchQueue。

区别:
主队列(Main):由系统自动创建,将其与我们的应用程序主线程关联,并启动我们的应用程序。
全局队列(Global):是一个具有指定服务质量类别的系统队列,如userInteractive、utility、background等。

用例:
主队列(Main):主要用于UI任务,尝试在主队列上同步执行工作项会导致死锁。
全局队列(Global):主要用于长时间运行的任务,比如进行网络调用。

在这种情况下,两者的代码将打印相同的输出,因为您使用sync函数调用全局dispatch,这意味着线程将等待直到您的任务完成,然后才继续执行下一行。如果您使用async,结果将不同,就像这样:

print(1)
DispatchQueue.main.async {
    print(2)
}
print(3)

//输出:1, 3, 2
英文:

First of all we need to understand what is DispatchQueue. Briefly DispatchQueue is
An object that manages the execution of tasks serially or concurrently on our app's main thread or on a background thread (for more). We can create dispatchQueue associated with the main (MAIN) thread or getting the global (GLOBAL) system queue with the specified quality-of-service class.
Briefly that's mean both of them (MAIN & GLOBAL) are dispatchQueue.

Differences:<br>
Main: automatically created by system, it associates it with our application’s main thread and starts our app with it.<br>
Global: is a system queue with the specified quality-of-service class like userInteractive, utility, background etc.

Use cases: <br>
Main: mostly used for UI tasks and attempting to synchronously execute a work item on the main queue results in deadlock.<br>
Global: mostly long running task, such as making network call<br>
In this case both of codes will print same output because you calling global dispatch with sync func not async thats mean thread will wait until your case finishes then moves to next line, if you use async result will be different like here

print(1)
DispatchQueue.main.async {
    print(2)
}
print(3)

//prints: 1,3,2

答案2

得分: 0

输出可能看起来相同,但实际上代码执行方式有所不同。在第一个代码片段中,打印语句按照编写顺序依次执行。

在第二个代码片段中,首先执行语句 print("1") 并将 "1" 打印到控制台。然后遇到 DispatchQueue.global().sync 块。此块中的代码将在全局队列上同步执行,这意味着它将在一个单独的线程上运行,但会阻塞当前线程,直到完成。在 DispatchQueue.global().sync 块内部,执行 print("k"),将 "k" 打印到控制台。在 sync 块完成后,执行继续到下一行,即 print("2"),将 "2" 打印到控制台。类似地,接下来执行 print("3")print("4"),分别打印 "3" 和 "4"。

因此,这两个代码片段的输出将是:

1
k
2
3
4
英文:

The output may appear the same, but there is actually a difference in how the code is executed.In the first code snippet, the print statements are executed sequentially, one after another, in the order they are written.

In the second code snippet The statement print(&quot;1&quot;) is executed first and prints "1" to the console.The DispatchQueue.global().sync block is encountered. The code inside this block will be executed synchronously on a global queue, which means it will run on a separate thread but will block the current thread until it completes.Inside the DispatchQueue.global().sync block, print(&quot;k&quot;) is executed, printing "k" to the console.After the sync block completes, the execution continues to the next line, which is print(&quot;2&quot;). It prints "2" to the console.
Similarly, print(&quot;3&quot;) and print(&quot;4&quot;) are executed next, printing "3" and "4" respectively.

So the output for the both code snippet will be

1
k
2
3
4

答案3

得分: 0

考虑你的第二个示例:

print("1")
DispatchQueue.global().sync {
    print("k")
}
print("2")
print("3")
print("4")

sync 调用会阻塞当前线程,并将 print("k") 分派到默认的全局队列,当它完成时,将在 print("2") 行继续执行。表面上,这意味着 print("k") 将在从默认全局队列的线程池中拉取的工作线程上运行,并且当前线程将被阻塞,直到它完成。你会看到按照这个顺序打印出 "1"、"k"、"2"、"3" 和 "4"。

然而,实际上,由于它是使用 sync 调度的,print("k") 实际上会在当前线程上运行。乍一看可能会感到困惑,但它实际上 不会 在全局队列的线程池上运行。正如 sync 文档 中所说:

作为性能优化,此函数尽可能在当前线程上执行块…

它这样做是因为从一个线程切换到另一个线程是有成本的。鉴于 sync 无论如何会阻塞当前线程,GCD 通过在 当前线程上 同步运行全局队列中的代码来避免尽可能减少昂贵的上下文切换。虽然有一些例外情况(例如,在分派 主队列时,与上述代码段无关),但通常情况下,sync 到全局队列只会在当前线程上同步运行它。

因此,即使没有这种优化,你的两个示例都会按顺序打印出 "1"、"k"、"2"、"3" 和 "4"。但由于这种巧妙的优化,你的两个代码片段实际上比你可能怀疑的更相似。事实上,它们都只是在当前线程上连续运行这五个 print 语句。


你说你想了解 sync 如何与 global 队列一起工作。实际情况是,你几乎永远不会使用 sync 将任务调度到全局队列。全局队列的主要目的是将某些耗时而同步的工作从当前线程中移出,例如,以避免阻塞主线程。因此,你几乎永远不会使用 sync 来在全局队列上运行任务,因为这样做会阻塞你本来想要避免阻塞的线程。

如果你想知道何时可能会使用 sync,一个常见的用例是同步访问某些共享资源。也许你有一些从不同线程访问的状态变量,你可能会使用 sync 来同步访问这些变量。但你不会在全局队列上使用它(因为它是并发的,不支持栅栏,对于同步没有用处)。我们会使用私有队列进行同步(或锁定或者 actors,但不是全局队列)。这只是一个例子,但简而言之,只有在绝对需要阻塞当前线程以便其他队列执行其工作时才使用 sync。否则,最好使用 async。如果你发现自己使用 sync,请始终问自己是否真的需要在分派的代码运行时阻塞当前线程。

英文:

Consider your second example:

print(&quot;1&quot;)
DispatchQueue.global().sync {
    print(&quot;k&quot;)
}
print(&quot;2&quot;)
print(&quot;3&quot;)
print(&quot;4&quot;)

The sync call blocks the current thread and dispatches the print(&quot;k&quot;) to the default global queue, and when it finishes, it will resume execution at the print(&quot;2&quot;) line. Ostensibly, this means that the print(&quot;k&quot;) would run on a worker thread pulled from the default global queue’s thread pool and that the current thread will be blocked until that finishes. You will see it print “1”, “k”, “2”, “3”, and “4”, in that order.

In reality, though, because it was dispatched with sync, the print(&quot;k&quot;) will actually run on the current thread. It might seem confusing at first glance, but it does not run on a thread from the global queue’s thread pool. As the sync documentation says:

> As a performance optimization, this function executes blocks on the current thread whenever possible…

It does this because there is a cost associated with switching from one thread to another. Given that sync would block the current thread anyway, GCD does this clever little optimization whereby it runs the code synchronously dispatched to the global queue on the current thread, thereby avoiding the costly context switch where possible. There are exceptions to this optimization (e.g., notably when dispatching to the main queue, which is irrelevant to the above code snippet), but generally sync to the global queue just runs it synchronously on the current thread.

So, even without this optimization, both of your examples will print the output of “1”, “k”, “2”, “3”, and “4”. But, because of this clever optimization, your two code snippets are actually far more similar than one might have otherwise suspected. Effectively, they both just run these five print statements, in succession, on the current thread.


You said that you want to understand how sync works with global queues. The reality is that you practically never dispatch with sync to a global queue. The primary purpose of the global queue is to get something slow and synchronous off the current thread, e.g., to avoid blocking the main thread. So one would practically never use sync to run something on a global queue, because you would end up just blocking the thread that you wanted to avoid blocking.

If you are wondering where you might actually use sync, a common use-case is for synchronizing access to some shared resource. Perhaps you have some state variable that you access from different threads, you might use sync to synchronize your access to that. But you would never use a global queue for that (since it is concurrent and doesn't support barriers, it is of no use for synchronization). We would use a private queue for synchronization (or locks or actors, but not global queues). This is just one example, but, in short, there are special use-cases for sync to private dispatch queues, but rarely with global queues.

In short, only use sync if it is truly essential to block the current thread while the other queue does its work. Otherwise, it is prudent to use async. If you find yourself reaching for sync, always ask yourself whether you really need to block the current thread while the dispatched code runs.

huangapple
  • 本文由 发表于 2023年6月12日 20:51:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76456852.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定