关于C#:Swift变量是原子的吗?

Are Swift variables atomic?

在Objective-C中,原子和非原子性质之间存在区别:

1
2
@property (nonatomic, strong) NSObject *nonatomicObject;
@property (atomic, strong) NSObject *atomicObject;

据我所知,您可以安全地从多个线程读取和写入定义为原子的属性,而同时从多个线程写入和访问非原子属性或ivar可能会导致未定义的行为,包括错误的访问错误。

所以如果在swift中有这样的变量:

1
var object: NSObject

我可以安全地并行读写这个变量吗?(不考虑这样做的实际意义)。


由于没有可用的低级文档,所以假设这一点很早,但您可以从组装中学习。漏斗拆卸器是一个很好的工具。

1
2
3
4
@interface ObjectiveCar : NSObject
@property (nonatomic, strong) id engine;
@property (atomic, strong) id driver;
@end

分别将objc_storeStrongobjc_setProperty_atomic用于非原子和原子,其中

1
2
3
4
5
class SwiftCar {
    var engine : AnyObject?    
    init() {
    }
}

使用libswift_stdlib_core中的swift_retain,显然没有内置线程安全性。

我们可以推测,以后可能会引入其他关键字(类似于@lazy)。

更新日期:2015年7月20日:根据这个关于singletons的blogpost,swift环境可以使某些情况对您来说是线程安全的,即:

1
2
3
4
5
6
7
8
class Car {
    static let sharedCar: Car = Car() // will be called inside of dispatch_once
}

private let sharedCar: Car2 = Car2() // same here
class Car2 {

}

更新日期:2016年5月25日:密切关注Swift Evolution Proposal https://github.com/apple/Swift-Evolution/blob/master/proposals/0030-property-behavior-decls.md-看起来您可以自己实施@atomic行为。


Swift没有关于线程安全的语言结构。假设您将使用提供的库来进行您自己的线程安全管理。在实现线程安全方面有大量的选项,包括pthread mutex、nslock和作为mutex机制的调度同步。参见Mike Ash最近关于这个主题的帖子:https://mike ash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html所以直接回答你的问题"我能安全地读写这个变量吗?"不是。


回答这个问题可能还为时过早。目前Swift缺少访问修饰符,因此没有明显的方法来添加代码来管理getter/setter属性周围的并发性。此外,Swift语言似乎还没有关于并发性的任何信息!(它还缺少kvo等…)

我认为这个问题的答案将在未来的版本中变得清晰。


细节

  • 代码9.1,Swift 4
  • Xcode 10.2.1(10E1001),Swift 5

链接

  • apple.developer.com发送
  • 中央调度中心(GCD)和swift 3中的调度队列
  • 在中创建线程安全数组迅捷
  • Mutexes与斯威夫特的封闭俘虏

实现类型

  • 原子阵列
  • 原子数

主要思想

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Example {

    private lazy var semaphore = DispatchSemaphore(value: 1)

    func executeThreadSafeFunc1() {
        // Lock access. Only first thread can execute code below.
        // Other threads will wait until semaphore.signal() will execute
        semaphore.wait()
        // your code
        semaphore.signal()         // Unlock access
    }

    func executeThreadSafeFunc2() {
        // Lock access. Only first thread can execute code below.
        // Other threads will wait until semaphore.signal() will execute
        semaphore.wait()
        DispatchQueue.global(qos: .background).async {
            // your code
            self.semaphore.signal()         // Unlock access
        }
    }
}

原子访问示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
class Atomic {

    let dispatchGroup = DispatchGroup()
    private var variable = 0

    // Usage of semaphores

    func semaphoreSample() {

        // value: 1 - number of threads that have simultaneous access to the variable
        let atomicSemaphore = DispatchSemaphore(value: 1)
        variable = 0

        runInSeveralQueues { dispatchQueue  in
            // Only (value) queqes can run operations betwen atomicSemaphore.wait() and atomicSemaphore.signal()
            // Others queues await their turn
            atomicSemaphore.wait()            // Lock access until atomicSemaphore.signal()
            self.variable += 1
            print("\(dispatchQueue), value: \(self.variable)")
            atomicSemaphore.signal()          // Unlock access
        }

        notifyWhenDone {
            atomicSemaphore.wait()           // Lock access until atomicSemaphore.signal()
            print("variable = \(self.variable)")
            atomicSemaphore.signal()         // Unlock access
        }
    }

    // Usage of sync of DispatchQueue

    func dispatchQueueSync() {
        let atomicQueue = DispatchQueue(label:"dispatchQueueSync")
        variable = 0

        runInSeveralQueues { dispatchQueue  in

            // Only queqe can run this closure (atomicQueue.sync {...})
            // Others queues await their turn
            atomicQueue.sync {
                self.variable += 1
                print("\(dispatchQueue), value: \(self.variable)")
            }
        }

        notifyWhenDone {
            atomicQueue.sync {
                print("variable = \(self.variable)")
            }
        }
    }

    // Usage of objc_sync_enter/objc_sync_exit

    func objcSync() {
        variable = 0

        runInSeveralQueues { dispatchQueue  in

            // Only one queqe can run operations betwen objc_sync_enter(self) and objc_sync_exit(self)
            // Others queues await their turn
            objc_sync_enter(self)                   // Lock access until objc_sync_exit(self).
            self.variable += 1
            print("\(dispatchQueue), value: \(self.variable)")
            objc_sync_exit(self)                    // Unlock access
        }

        notifyWhenDone {
            objc_sync_enter(self)                   // Lock access until objc_sync_exit(self)
            print("variable = \(self.variable)")
            objc_sync_exit(self)                    // Unlock access
        }
    }
}

// Helpers

extension Atomic {

    fileprivate func notifyWhenDone(closure: @escaping ()->()) {
        dispatchGroup.notify(queue: .global(qos: .utility)) {
            closure()
            print("All work done")
        }
    }

    fileprivate func runInSeveralQueues(closure: @escaping (DispatchQueue)->()) {

        async(dispatch: .main, closure: closure)
        async(dispatch: .global(qos: .userInitiated), closure: closure)
        async(dispatch: .global(qos: .utility), closure: closure)
        async(dispatch: .global(qos: .default), closure: closure)
        async(dispatch: .global(qos: .userInteractive), closure: closure)
    }

    private func async(dispatch: DispatchQueue, closure: @escaping (DispatchQueue)->()) {

        for _ in 0 ..< 100 {
            dispatchGroup.enter()
            dispatch.async {
                let usec = Int(arc4random()) % 100_000
                usleep(useconds_t(usec))
                closure(dispatch)
                self.dispatchGroup.leave()
            }
        }
    }
}

用法

1
2
3
Atomic().semaphoreSample()
//Atomic().dispatchQueueSync()
//Atomic().objcSync()

结果

enter image description here