Swift: 如何将worker放到单独的线程中,在主视图中显示结果?
Swift: How to put worker into separate thread, show results in main view?
我想知道是否有一种简单的方法可以做到这一点:将一个long-运行 worker 放到一个单独的线程中,以免阻塞UI。并在主视图中显示结果。在 SwiftUI 或 UI 工具包下。我在网上找到的都非常复杂。还是 Swift 中有完全不同的方法?
我制作了一个简约的 Python 程序来展示我想做什么。它在 MacOS 中显示 WiFi 信号强度。
import time
import sys
import subprocess
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout
from PyQt5.QtCore import QObject, pyqtSignal, QThread
class Worker(QObject):
send_output = pyqtSignal(str)
def __init__(self):
super().__init__()
def worker(self):
while True:
_out = subprocess.check_output(
["/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport",
"-I"])\
.decode("utf-8")
self.send_output.emit("RSSI: " + _out.splitlines()[0][-3:] + " dBm")
time.sleep(2)
class MainWindow(QWidget):
do_work = pyqtSignal(object)
def __init__(self):
super().__init__()
self.label = QLabel()
layout = QHBoxLayout()
self.setLayout(layout)
layout.addWidget(self.label)
self.show()
self.app_thread = QThread()
self.app = Worker()
self.app.moveToThread(self.app_thread)
self.app.send_output.connect(self.output_label)
self.app_thread.started.connect(self.app.worker)
self.app_thread.start()
def output_label(self, _str):
self.label.setText(_str)
if __name__ == '__main__':
application = QApplication(sys.argv)
mainwindow = MainWindow()
sys.exit(application.exec())
我现在正在尝试进入 Swift。这很令人兴奋,但确实是一件大事。提前致谢!
这是一个非常宽泛的问题,所以我也将广泛地回答它。
是的,您可以 运行 在单独的线程中执行任务,然后 return 将数据发送到主线程。有多种方法可以做到这一点,一种常见的方法是 DispatchQueue
.
让我们举一个过度简化的例子(绝对不是真实世界的代码):
struct ContentView : View {
@State var result = ""
var body: some View {
Text("Result: \(result)")
.onAppear {
DispatchQueue.global(qos: .background).async {
//do your long-running code
var innerResult = 0
for i in 0...1000000 {
innerResult += 5
print(i)
//use DispatchQueue.main.async here if you want to update during the task
}
//update at the end:
DispatchQueue.main.async {
result = "Done! \(innerResult)"
}
}
}
}
}
在这个例子中,当视图出现时,后台线程中的任务是 运行,通过 DispatchQueue
。在这种情况下,我只是利用了这样一个事实,即如果完成一百万次,打印到控制台是一项相当昂贵的操作。
完成后,它会分派回主线程并更新状态变量中的结果。
DispatchQueue
不特定于 UIKit 或 SwiftUI——使用 SwiftUI 将演示放在一起是最简单的。
如果您真的要开始编写代码来执行此操作,您可能想要研究一下 DispatchQueues 的工作原理,包括使用哪个队列、是否创建自己的队列、是否希望连续完成任务,等,但出于广泛问题的目的,即可以做到这一点(以及有多容易),这至少展示了基础知识。
我想知道是否有一种简单的方法可以做到这一点:将一个long-运行 worker 放到一个单独的线程中,以免阻塞UI。并在主视图中显示结果。在 SwiftUI 或 UI 工具包下。我在网上找到的都非常复杂。还是 Swift 中有完全不同的方法?
我制作了一个简约的 Python 程序来展示我想做什么。它在 MacOS 中显示 WiFi 信号强度。
import time
import sys
import subprocess
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout
from PyQt5.QtCore import QObject, pyqtSignal, QThread
class Worker(QObject):
send_output = pyqtSignal(str)
def __init__(self):
super().__init__()
def worker(self):
while True:
_out = subprocess.check_output(
["/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport",
"-I"])\
.decode("utf-8")
self.send_output.emit("RSSI: " + _out.splitlines()[0][-3:] + " dBm")
time.sleep(2)
class MainWindow(QWidget):
do_work = pyqtSignal(object)
def __init__(self):
super().__init__()
self.label = QLabel()
layout = QHBoxLayout()
self.setLayout(layout)
layout.addWidget(self.label)
self.show()
self.app_thread = QThread()
self.app = Worker()
self.app.moveToThread(self.app_thread)
self.app.send_output.connect(self.output_label)
self.app_thread.started.connect(self.app.worker)
self.app_thread.start()
def output_label(self, _str):
self.label.setText(_str)
if __name__ == '__main__':
application = QApplication(sys.argv)
mainwindow = MainWindow()
sys.exit(application.exec())
我现在正在尝试进入 Swift。这很令人兴奋,但确实是一件大事。提前致谢!
这是一个非常宽泛的问题,所以我也将广泛地回答它。
是的,您可以 运行 在单独的线程中执行任务,然后 return 将数据发送到主线程。有多种方法可以做到这一点,一种常见的方法是 DispatchQueue
.
让我们举一个过度简化的例子(绝对不是真实世界的代码):
struct ContentView : View {
@State var result = ""
var body: some View {
Text("Result: \(result)")
.onAppear {
DispatchQueue.global(qos: .background).async {
//do your long-running code
var innerResult = 0
for i in 0...1000000 {
innerResult += 5
print(i)
//use DispatchQueue.main.async here if you want to update during the task
}
//update at the end:
DispatchQueue.main.async {
result = "Done! \(innerResult)"
}
}
}
}
}
在这个例子中,当视图出现时,后台线程中的任务是 运行,通过 DispatchQueue
。在这种情况下,我只是利用了这样一个事实,即如果完成一百万次,打印到控制台是一项相当昂贵的操作。
完成后,它会分派回主线程并更新状态变量中的结果。
DispatchQueue
不特定于 UIKit 或 SwiftUI——使用 SwiftUI 将演示放在一起是最简单的。
如果您真的要开始编写代码来执行此操作,您可能想要研究一下 DispatchQueues 的工作原理,包括使用哪个队列、是否创建自己的队列、是否希望连续完成任务,等,但出于广泛问题的目的,即可以做到这一点(以及有多容易),这至少展示了基础知识。