stackoverflow 上的大多数答案都暗示同步与异步行为与串行与并发队列概念差异非常相似。就像@Roope 的第一条评论中的链接一样
我开始认为串行和并发与DispatchQueue同步/异步有关如何在线程上执行操作。我对吗?
就像如果我们已经得到DQ.main.sync那么任务/操作闭包将以同步方式在此串行(主)队列上执行。而且,如果我DQ.main.async这样做,任务将在其他后台队列上异步获取,并且在完成后将返回主线程的控制权。而且,由于 main 是一个串行队列,因此在当前关闭任务完成执行之前,它不会让任何其他任务/操作进入执行状态/开始执行。
然后,
DQ.global().sync将在已分配其任务/操作的线程上同步执行任务,即,它将通过阻止该特定线程上的任何上下文切换来阻止该线程执行任何其他任务/操作。并且,由于 global 是一个并发队列,因此无论先前任务/操作的执行状态如何,它都会继续将其中存在的任务置于执行状态。
DQ.global().async将允许在已执行操作闭包的线程上进行上下文切换
这是对上述调度队列和同步与异步的正确解释吗?
grand-central-dispatch ios swift ios-multithreading dispatch-queue
在这种情况下我需要某种显式同步吗?
class A {
let val: Int;
init(_ newVal: Int) {
val = newVal
}
}
public class B {
var a: A? = nil
public func setA() { a = A(0) }
public func hasA() -> Bool { return a != nil }
}
Run Code Online (Sandbox Code Playgroud)
B类中还有另一种方法:
public func resetA() {
guard hasA() else { return }
a = A(1)
}
Run Code Online (Sandbox Code Playgroud)
setA()并且resetA()可以从任何线程以任何顺序调用。
我知道可能存在竞争条件,如果同时一个线程调用setA()和另一个线程调用resetA(),则结果无法确定:val要么是0,要么是1,但我不在乎:无论如何,hasA()将返回 true,won'它吗?
如果 …
我在 Swift 中创建了一个“锁”,并为我的 Swift 类创建了一个使用该锁的 Atomic 属性包装器,因为 Swift 缺少 ObjC 的atomic属性属性。
当我在启用线程清理器的情况下运行我的测试时,它总是捕获使用我的 Atomic 属性包装器的属性上的数据竞争。
唯一有效的是将属性包装器的声明更改为一个类而不是一个结构体,这里的主要问题是:为什么它有效!
我print在属性包装器中添加了s 并添加了 lockinit来跟踪创建的对象数量,它与 struct/class 相同,尝试在另一个项目中重现该问题,但也没有用。但是我会添加与问题类似的文件,并让我知道它为什么起作用的任何猜测。
锁
public class SwiftLock {
init() { }
public func sync<R>(execute: () throws -> R) rethrows -> R {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
return try execute()
}
}
Run Code Online (Sandbox Code Playgroud)
原子属性包装器
@propertyWrapper struct Atomic<Value> {
let lock: SwiftLock
var value: Value
init(wrappedValue: Value, lock: SwiftLock=SwiftLock()) {
self.value = wrappedValue
self.lock = …Run Code Online (Sandbox Code Playgroud) MacOS Sonoma 14.0、iOS 17.0、Xcode 15。应用程序部署到手机。按下应用程序上的某个按钮,它就会弹出一条消息,我刚刚开始看到这条消息,但不知道如何解决。
\n“[VERBOSE-2:shell.cc(1004)] \'dev.fluttercommunity.plus/sensors/accelerometer\' 通道在非平台线程上从本机向 Flutter 发送了一条消息。平台通道消息必须在平台线程。不这样做可能会导致数据丢失或崩溃,并且必须在创建该通道的插件或应用程序代码中进行修复。”
\n“[VERBOSE-2:shell.cc(1004)] \'dev.fluttercommunity.plus/sensors/gyrscope\' 通道在非平台线程上从本机向 Flutter 发送了一条消息。平台通道消息必须在平台线程。不这样做可能会导致数据丢失或崩溃,并且必须在创建该通道的插件或应用程序代码中进行修复。”
\n正在下载 darwin-arm64/font-subset 工具... 79ms\n[\xe2\x9c\x93] Flutter(频道稳定,3.13.6,在 macOS 14.0 23A344 darwin-arm64,区域设置 en-US)\n\xe2\ x80\xa2 Flutter 版本 3.13.6 稳定在 /Users/wjmetcalfiii/Downloads/flutter\n\xe2\x80\xa2 上游存储库https://github.com/flutter/flutter.git \n\xe2\x80\ xa2 框架修订版 ead455963c(2 周前),2023-09-26 18:28:17 -0700\n\xe2\x80\xa2 引擎修订版 a794cf2681\n\xe2\x80\xa2 Dart 版本 3.1.3\n\xe2 \x80\xa2 开发工具版本 2.25.0
\n[\xe2\x9c\x93] Android 工具链 - 为 Android 设备开发(Android SDK 版本 34.0.0)\n\xe2\x80\xa2 Android SDK,位于 /Users/wjmetcalfiii/Library/Android/sdk\n\xe2\x80 \xa2 平台 android-34,构建工具 34.0.0\n\xe2\x80\xa2 Java 二进制文件位于:/Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java\n\xe2\x80 \xa2 Java 版本 OpenJDK 运行时环境(内部版本 17.0.6+0-17.0.6b829.9-10027231)\n\xe2\x80\xa2 接受所有 Android …
我相信 XCode 错误地报告了我的 Swift Access Race SynchronizedDictionary- 或者是这样?
我的SynchronizedDictionary看起来像这样:
public struct SynchronizedDictionary<K: Hashable, V> {
private var dictionary = [K: V]()
private let queue = DispatchQueue(
label: "SynchronizedDictionary",
qos: DispatchQoS.userInitiated,
attributes: [DispatchQueue.Attributes.concurrent]
)
public subscript(key: K) -> V? {
get {
return queue.sync {
return self.dictionary[key]
}
}
mutating set {
queue.sync(flags: .barrier) {
self.dictionary[key] = newValue
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下测试代码将触发“Swift Access Race”问题(当为该方案打开Thread Sanitizer时):
var syncDict = SynchronizedDictionary<String, String>()
let setExpectation = XCTestExpectation(description: "set_expectation")
let getExpectation …Run Code Online (Sandbox Code Playgroud) xcode multithreading race-condition swift ios-multithreading
在我的游戏引擎中,我可以选择启用多线程渲染。在iOS 12(及更低版本)中,我的应用没有崩溃。
我没有更改代码中的任何内容,但是在iOS 13上崩溃。
2019-10-02 11:36:07.931 SimpleGame[293:11150] ---> (Inner Exception #0) Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: threading violation: expected the main thread
Oct 2 09:57:48 GL04207-MT SimpleGame[923] <Notice>: 0 CoreFoundation 0x00000001aace1c44 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 1256516
Oct 2 09:57:48 GL04207-MT SimpleGame[923] <Notice>: 1 libobjc.A.dylib 0x00000001aa9fc0c8 objc_exception_throw + 60
Oct 2 09:57:48 GL04207-MT SimpleGame[923] <Notice>: 2 CoreFoundation 0x00000001aabde75c 97285ACB-7B21-393A-ABF6-03F1DBB5D2A2 + 194396
Oct 2 09:57:48 GL04207-MT SimpleGame[923] <Notice>: 3 Foundation 0x00000001ab01f960 672CF0CB-4951-3B91-89DF-55E953AEA00F + 571744
Oct 2 09:57:48 GL04207-MT SimpleGame[923] <Notice>: 4 FrontBoardServices …Run Code Online (Sandbox Code Playgroud) 我已经阅读了有关在中进行优化Arrays和其他数据结构的写时复制概念Swift。
我想知道的是写时复制在多线程环境中如何工作。
let arr1 = [1, 2, 3, 4]
let arr2 = arr1
arr1.withUnsafeBytes { print("arr1:", $0.baseAddress) } //0x000060000007ee60
arr2.withUnsafeBytes { print("arr2:", $0.baseAddress) } //0x000060000007ee60
DispatchQueue.global(qos: .default).async {
let arr3 = arr1
arr3.withUnsafeBytes { print("arr3:", $0.baseAddress) } //0x000060000007ee60
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,arr1并且arr2最初具有与中期望的相同的地址copy-on-write。不过,arr3成也共享相同的存储arr1和arr2虽然是在不同的线程执行。
据我所知,每个线程都有不同的堆栈分配。那为什么arr3仍然共享相同的位置?
有人可以解释一下它是如何工作的。
我正在开发的应用程序几乎每个屏幕上都有谷歌地图。为了节省内存,我在各处重复使用相同的谷歌地图视图。问题是,当您弹出视图控制器时,您可以看到地图所在的空白区域。为了解决这个问题,我在删除地图之前对其进行了屏幕截图并添加为背景。但还有一个问题,在 iPhoneX 上截图大约需要 0.3 秒(我想在旧手机上情况更糟)。有没有办法在后台线程上截取 UIView 的屏幕截图?
我正在尝试使用 Swift 5 中的 AVAudioPlayer 实例从后台线程播放声音。大多数时候,它会成功。但有时音频播放器无法播放声音。
\n\n看起来好像连续调用 play() 两次,或者调用prepareToPlay() 然后连续调用 play() 以某种方式解决了问题,但这对我来说似乎不是一个好的解决方案,因为我担心这仍然不能解决问题不保证一定能播放成功。
\n\nfunc setUpAudio() {\n AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:error:"), with: AVAudioSession.Category.playback)\n try! AVAudioSession.sharedInstance().setActive(true)\n NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption), name: AVAudioSession.interruptionNotification, object: nil)\n}\n\nfunc startNewAudio() {\n let destinationData = Destinations.getDestinations()\n var audio = destinationData[currentStop]!["audio"] as! String\n if inTransit {\n audio = destinationData[Paths.tourOrder[currentIndex + 1]]!["transit_audio"] as! String\n }\n let sound = NSURL(fileURLWithPath: Bundle.main.path(forResource: audio, ofType: "mp3", inDirectory: "audio")!)\n try! audioPlayer = AVAudioPlayer(contentsOf: sound as URL)\n audioPlayer.delegate = self\n currentTime = 0\n setPlay()\n}\n\nfunc setPlay() {\n paused = …Run Code Online (Sandbox Code Playgroud) 当我尝试 GCD 函数时dispatch_barrier_async,它在由 创建的队列上按预期工作dispatch_queue_create,而当我将它放在由 创建的全局队列上时dispatch_get_global_queue,屏障似乎不再起作用= =,有人可以解释吗?谢谢~
演示图
我有一个在用户点击按钮时设置的本地通知。我想将此发送到后台线程,然后更新UI。这是一种安全的方法吗?
DispatchQueue.global(qos: .background).async { // sends registration to background queue
center.add(request, withCompletionHandler: {(error) in
if let error = error {
print(error)
}
})
DispatchQueue.main.async {
try! self.realm.write {
self.realm.add(task)
self.updateData()
}
}
}
Run Code Online (Sandbox Code Playgroud) 我知道在更新UI时我们必须调用主线程.但我不能解释我的队友为什么要这样做以及为什么Swift不会自动完成.
他们曾经像这样调用self.present():
self.present(alert, animated: true)
Run Code Online (Sandbox Code Playgroud)
但我知道你应该这样称呼它:
DispatchQueue.main.async {
self.present(alert, animated: true)
}
Run Code Online (Sandbox Code Playgroud)
我实际上想确保在主线程上总是调用该方法,但我不知道如何...另一个问题是:为什么我必须确保在主线程而不是Swift上调用此方法?当有人调用此方法时,总会有UI更新.
@available(iOS 5.0, *)
open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil)
Run Code Online (Sandbox Code Playgroud) 我目前正在 Xcode 10 Playground (Swift 5) 中测试此代码:
func one() {
let test = "bla"
two(test, completion: { (returned) in
print(returned)
})
}
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
DispatchQueue.main.async {
return completion("hi!")
}
}
//Also some slow stuff
DispatchQueue.main.async {
return completion(nil) //can't have this in "else"!
}
}
}
one()
Run Code Online (Sandbox Code Playgroud)
问题是“hi”和“nil”都被打印了。
如果我摆脱线程,它工作得很好,但有了它,它似乎在DispatchQueue.main.async第一个线程有机会返回之前到达第二个线程。
在我的实际代码中的“一些缓慢的东西”中还有很多事情发生,if但我不能依赖在调用第二次返回之前花费足够长的时间来返回。
如何实现这一点:让函数在后台线程中运行,但在主线程上仅返回一次(就像通常没有线程的代码一样)?
ios completionhandler swift ios-multithreading dispatch-queue