Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 如何避免与GCD DispatchWorkItem.notify的数据竞争?_Swift_Grand Central Dispatch_Thread Sanitizer - Fatal编程技术网

Swift 如何避免与GCD DispatchWorkItem.notify的数据竞争?

Swift 如何避免与GCD DispatchWorkItem.notify的数据竞争?,swift,grand-central-dispatch,thread-sanitizer,Swift,Grand Central Dispatch,Thread Sanitizer,对于XCode 8.3上的Swift 3.1,使用线程消毒剂运行以下代码会发现数据竞争(请参阅代码中的写和读注释): private func incrementAsync(){ 让item=DispatchWorkItem{[weak self]在 guard let strongSelf=self-else{return} strongSelf.x+=1//编辑(2019-01-07):正如@Rob在对该问题的评论中所提到的,这一点在最新版本的Xcode/Foundation中无法复制(我不

对于XCode 8.3上的Swift 3.1,使用线程消毒剂运行以下代码会发现数据竞争(请参阅代码中的写和读注释):

private func incrementAsync(){
让item=DispatchWorkItem{[weak self]在
guard let strongSelf=self-else{return}
strongSelf.x+=1//编辑(2019-01-07):正如@Rob在对该问题的评论中所提到的,这一点在最新版本的Xcode/Foundation中无法复制(我不再安装Xcode,我不会猜到版本号)。无需解决方法


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

  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
引入了一个before关系,并且在线程(在本例中是单个异步工作项)完成执行后安全地调用了
notify
,而
DispatchWorkItem.notify
不提供这种保证

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()
你完全正确! 增加最后期限

DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.01, execute: job)
你有

job notify
即使工作永远不会执行

notify与DispatchWorkItem的关闭捕获的任何数据的同步无关

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

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true


let group = DispatchGroup()
group.notify(queue: .main) {
    print("group notify")
}
看到结果了吗

group notify
!!!WTF!!!你还认为你在代码中解决了这场竞赛吗? 要同步任何读、写…请使用串行队列、屏障或信号量。调度组与beast:-)完全不同。您可以将多个任务分组,等待它们完成,或者在它们完成后接收通知

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")
印刷品

line1
line2
job 1 done
job 2 done
group notify

DispatchWorkItem
应保证单次执行不存在争用。针对您的情况的争用可能来自多次执行块,然后在单独的队列上发出通知(与工作队列不同步).如果您还没有看到,我认为
dispatch\u block\u notify()文档中对该行为的解释
更好。不过,它是一次执行。每次调用该方法时,它都会创建一个新的DispatchWorkItem,只执行一次。感谢您对文档的介绍,它确实提到了使用DispatchGroup!看起来swift文档在某些地方还不太流行。嗯,是的,您完全正确。我把自己搞糊涂了仔细想想。@mna请看我的“答案”。这不是关于线程安全数据的读写。我只是想解释一下为什么你错了,为什么你的回答不应该作为解决方案被接受。这一点已经无法体现。他们一直在修改和增强线程消毒剂,所以我怀疑这仅仅是TSAN早期迭代中的一个错误。
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")
line1
line2
job 1 done
job 2 done
group notify