GCD真的是线程安全的吗?

Hyu*_*nSu 2 thread-safety grand-central-dispatch swift

我研究过GCD和线程安全。在苹果文档中,GCD是Thread-Safe的,意味着多个线程可以访问。我了解了线程安全的含义,即每当多个线程访问某个对象时总是给出相同的结果。

我认为Thread-Safe和GCD的Thread-Safe的含义不一样,因为我测试了下面写的一些案例,将0到9999相加。

当我多次执行下面的代码时,“something.n”值不同。如果 GCD 是线程安全的,为什么“something.n”值不一样?

我真的很困惑..你能帮助我吗?我真的很想掌握线程安全!!!

class Something {
    var n = 0
}
class ViewController: UIViewController {
    let something = Something()
    var concurrentQueue = DispatchQueue(label: "asdf", attributes: .concurrent)
    override func viewDidLoad() {
        super.viewDidLoad()
        let group = DispatchGroup()
        for idx in 0..<10000 {
            concurrentQueue.async(group: group) {
                self.something.n += idx
            }
        }
    
        group.notify(queue: .main ) {
            print(self.something.n)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 5

你说:

\n
\n

我研究过GCD和线程安全。在苹果文档中,GCD是Thread-Safe的,意味着多个线程可以访问。我了解了线程安全的含义,即每当多个线程访问某个对象时总是给出相同的结果。

\n
\n

他们说的是同一件事。仅当可以安全地同时从不同线程调用代码块时,该代码块才是线程安全的(并且这种线程安全性是通过确保代码的关键部分不能与另一个线程同时在一个线程上运行来实现的)线)。

\n

但让我们明确一点:苹果并不是说如果你使用 GCD,你的代码就自动是线程安全的。是的,调度队列对象本身是线程安全的(即您可以从任何您想要的线程安全地调度到队列),但这并不意味着您自己的代码一定是线程安全的。如果一个\xe2\x80\x99s 代码同时从多个线程访问同一内存,则必须提供一个\xe2\x80\x99s 自己的同步,以防止与任何其他访问同时进行写入。

\n

在GCD 之前的《线程编程指南:同步》中,Apple 概述了同步代码的各种机制。您还可以使用 GCD 串行队列进行同步。如果使用并发队列,则使用 \xe2\x80\x9cbarrier\xe2\x80\x9d 进行写入操作即可实现线程安全。有关实现线程安全的各种方法,请参阅本答案的后半部分。

\n

但要明确的是,Apple 并没有引入 \xe2\x80\x9cthread-safe\xe2\x80\x9d 的不同定义。正如他们在上述指南中所说:

\n
\n

当谈到线程安全时,良好的设计是最好的保护。避免共享资源并最大限度地减少线程之间的交互可以降低这些线程相互干扰的可能性。然而,完全无干扰的设计并不总是可能的。在线程必须交互的情况下,您需要使用同步工具来确保它们在交互时安全地进行。

\n
\n

在引入 GCD 时发布的《并发编程指南:从线程迁移:消除基于锁的代码》中,Apple 表示:

\n
\n

对于线程代码,锁是同步对线程之间共享资源的访问的传统方法之一。...您可以创建一个队列来序列化访问该资源的任务,而不是使用锁来保护共享资源。

\n
\n

但他们并不是说只要使用GCD并发队列就可以自动实现线程安全,而是说只要仔细正确地使用GCD队列,就可以在不使用锁的情况下实现线程安全。

\n
\n

顺便说一句,Apple 提供了工具来帮助您诊断代码是否是线程安全的,即 Thread Sanitizer (TSAN)。请参阅尽早诊断内存、线程和崩溃问题

\n

  • 可以从任何您想要的线程访问调度队列,因为队列本身是线程安全的。这与我们使用 GCD 的代码是否是线程安全的问题完全不同。为此,我们必须对我们的代码进行推理,并弄清楚我们是否存在任何“数据竞争”(两个不同的线程可能试图同时访问相同的不同步资源)。如果我们确实存在数据竞争,我们可以通过多种方式解决这个问题,GCD 或其他方式。但不要将队列的线程安全性与代码的线程安全性混为一谈。 (2认同)