首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免使用GCD DispatchWorkItem.notify进行数据竞争?

如何避免使用GCD DispatchWorkItem.notify进行数据竞争?
EN

Stack Overflow用户
提问于 2017-03-31 01:49:22
回答 2查看 2.3K关注 0票数 3

在XCode 8.3上使用SWIFT3.1,使用线程清除器运行以下代码可以找到一个数据竞争(请参阅代码中的写和读注释):

代码语言:javascript
复制
  private func incrementAsync() {
    let item = DispatchWorkItem { [weak self] in
      guard let strongSelf = self else { return }
      strongSelf.x += 1 // <--- the write

      // Uncomment following line and there's no race, probably because print introduces a barrier
      //print("> DispatchWorkItem done")
    }
    item.notify(queue: .main) { [weak self] in
      guard let strongSelf = self else { return }
      print("> \(strongSelf.x)") // <--- the read
    }

    DispatchQueue.global(qos: .background).async(execute: item)
  }

这对我来说很奇怪,因为DispatchWorkItem的文档提到它允许:

接到他们完成的通知

这意味着,一旦完成了工作项的执行,就会调用notify回调。

因此,我预计,在happens-before的工作结束和通知关闭之间会存在happens-before关系。如果有正确的方法,那么使用DispatchWorkItem并使用这样一个注册的notify回调不会触发线程清除器错误的方法是什么?

我试着用notify注册item.notify(flags: .barrier, queue: .main) ...,但是竞争仍然存在(可能是因为标记只适用于同一个队列,文档很少显示.barrier标志的功能)。但是,即使使用flags: .barrier在同一个(后台)队列上调用notify作为工作项的执行,也会导致竞争。

如果您想尝试这一点,我在github上发布了完整的XCode项目:https://github.com/mna/TestDispatchNotify

这里有一个TestDispatchNotify计划,在没有tsan的情况下构建应用程序,而使用线程杀菌剂的TestDispatchNotify+Tsan则是激活的。

谢谢,马丁

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-03-31 02:00:15

编辑(2019-01-07):正如@Rob在关于这个问题的评论中提到的那样,不能再用Xcode/Foundation的最新版本来复制它了(我不再安装Xcode,我不会猜测版本号)。不需要任何解决办法。

看来我发现了。当组的分派项完成时,使用DispatchGroup.notify来获得通知,而不是DispatchWorkItem.notify,可以避免数据竞争。下面是没有数据竞争的相同的代码片段:

代码语言:javascript
复制
  private func incrementAsync() {
    let queue = DispatchQueue.global(qos: .background)

    let item = DispatchWorkItem { [weak self] in
      guard let strongSelf = self else { return }
      strongSelf.x += 1
    }

    let group = DispatchGroup()
    group.notify(queue: .main) { [weak self] in
      guard let strongSelf = self else { return }
      print("> \(strongSelf.x)")
    }
    queue.async(group: group, execute: item)
  }

因此,DispatchGroup引入了一个发生之前的关系,并且在线程(在本例中是一个异步工作项)完成执行之后安全地调用了notify,而DispatchWorkItem.notify没有提供这种保证。

票数 1
EN

Stack Overflow用户

发布于 2019-01-06 14:39:38

代码语言:javascript
复制
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

var job = DispatchWorkItem {
    for i in 0..<3 {
        DispatchQueue.main.async {
            print("job", i)
        }
    }
    DispatchQueue.main.async {
        print("job done")
    }
}
job.notify(queue: .main) {
    print("job notify")
}

DispatchQueue.global(qos: .background).asyncAfter(deadline: .now(), execute: job)
usleep(100)
job.cancel()

如果你猜这个片段是打印出来的

代码语言:javascript
复制
job 0
job 1
job 2
job done
job notify

你绝对是对的!增加deadLine ..。

代码语言:javascript
复制
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.01, execute: job)

而且你有

代码语言:javascript
复制
job notify

即使任务执行从来没有

notify与DispatchWorkItem的闭包所捕获的任何数据的同步没有任何关系。

让我用DispatchGroup!来试试这个例子

代码语言:javascript
复制
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true


let group = DispatchGroup()
group.notify(queue: .main) {
    print("group notify")
}

看看结果

代码语言:javascript
复制
group notify

!哇!你还认为你解决了代码中的竞争吗?来同步任何读、写.使用串行队列、屏障或信号量。分派组是完全不同的:-)与分派组一起,您可以将多个任务组合在一起,等待它们完成,或者在它们完成后接收通知。

代码语言:javascript
复制
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let job1 = DispatchWorkItem {
    sleep(1)
    DispatchQueue.main.async {
        print("job 1 done")
    }
}
let job2 = DispatchWorkItem {
    sleep(2)
    DispatchQueue.main.async {
        print("job 2 done")
    }
}
let group = DispatchGroup()
DispatchQueue.global(qos: .background).async(group: group, execute: job1)
DispatchQueue.global(qos: .background).async(group: group, execute: job2)

print("line1")
group.notify(queue: .main) {
    print("group notify")
}
print("line2")

版画

代码语言:javascript
复制
line1
line2
job 1 done
job 2 done
group notify
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43131084

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档