我遇到了一个问题,在多线程环境中,如果在访问属性时遇到竞争条件,则初始化闭包的惰性实例变量会多次调用初始化闭包。一个例子是:
import Darwin
import Dispatch
func doSomethingIntense() {
usleep(100000)
}
var counter: Int = 0
class Cat {
lazy var sound: String = {
doSomethingIntense()
let isFive = counter == 5
counter += 1
print("Getting \(Unmanaged.passUnretained(self).toOpaque()).sound")
return isFive ? "doctorate denied" : "meow"
}()
}
let cat = Cat()
let group = DispatchGroup()
for _ in 1..<10 {
if #available(OSX 10.10, *) {
usleep(100);
DispatchQueue.global(qos: .background).async {
group.enter()
print("The cat says \(cat.sound)")
group.leave()
}
}
}
group.wait()
示例输出为:
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
Getting 0x00007fd4f170f360.sound
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says doctorate denied
当我想要输出
时Getting 0x00007fd4f170f360.sound
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
The cat says meow
目前,我的解决方案是两个bool,initingSound
和finishedInitingSound
,以及互斥锁和条件变量。互斥锁保护initingSound
,等待条件检查finishedInitingSound
:
import Darwin
import Dispatch
func doSomethingIntense() {
usleep(100000)
}
var counter: Int = 0
class Cat {
private var initingSound = false
private var finishedInitingSound = false
private var soundMutex = pthread_mutex_t()
private var soundCondition = pthread_cond_t()
lazy var sound: String = {
pthread_mutex_lock(&self.soundMutex)
if self.initingSound {
while !self.finishedInitingSound {
pthread_cond_wait(&self.soundCondition, &self.soundMutex)
}
pthread_mutex_unlock(&self.soundMutex)
return self.sound
}
self.initingSound = true
pthread_mutex_unlock(&self.soundMutex)
doSomethingIntense()
let isFive = counter == 5
counter += 1
print("Getting \(Unmanaged.passUnretained(self).toOpaque()).sound")
self.finishedInitingSound = true
pthread_cond_broadcast(&self.soundCondition)
return isFive ? "doctorate denied" : "meow"
}()
init() {
pthread_mutex_init(&soundMutex, nil)
pthread_cond_init(&soundCondition, nil)
}
deinit {
pthread_mutex_destroy(&soundMutex)
pthread_cond_destroy(&soundCondition)
}
}
let cat = Cat()
let group = DispatchGroup()
for _ in 1..<10 {
if #available(OSX 10.10, *) {
usleep(100);
DispatchQueue.global(qos: .background).async {
group.enter()
print("The cat says \(cat.sound)")
group.leave()
}
}
}
group.wait()
这个解决方案看起来并不敏捷&#39;或者在GCD提出的设计方案中。如果没有在Swift 3中删除它,似乎dispatch_once可能会有用。
顺便说一句,在快速的开发中是否有讨论使这些惰性属性进行原子初始化(注意,我只是在谈论它何时初始化,而不是让所有惰性变量始终是原子的)。