长周期区块应用

2023-11-26

我的应用程序中有以下循环

var maxIterations: Int = 0

func calculatePoint(cn: Complex) -> Int {

    let threshold: Double = 2
    var z: Complex = .init(re: 0, im: 0)
    var z2: Complex = .init(re: 0, im: 0)
    var iteration: Int = 0

    repeat {
        z2 = self.pow2ForComplex(cn: z)
        z.re = z2.re + cn.re
        z.im = z2.im + cn.im
        iteration += 1
    } while self.absForComplex(cn: z) <= threshold && iteration < self.maxIterations

    return iteration
}

循环执行过程中会显示彩虹轮。我如何管理该应用程序仍然响应 UI 操作? 请注意,我在代码的不同部分更新了 NSProgressIndicator,在循环运行时未更新(未显示进度)。 我怀疑这与调度有关,但我对此很“陌生”。我非常感谢任何帮助。 谢谢。


要异步分派某些内容,请调用async在适当的队列上。例如,您可以更改此方法以在全局后台队列上进行计算,然后在主队列上报告结果。顺便说一句,当您这样做时,您从立即返回结果转变为使用完成处理程序闭包,异步方法将在计算完成时调用该闭包:

func calculatePoint(_ cn: Complex, completionHandler: @escaping (Int) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async {
        // do your complicated calculation here which calculates `iteration`

        DispatchQueue.main.async {
            completionHandler(iteration)
        }
    }
}

你会这样称呼它:

// start NSProgressIndicator here

calculatePoint(point) { iterations in
    // use iterations here, noting that this is called asynchronously (i.e. later)

    // stop NSProgressIndicator here
}

// don't use iterations here, because the above closure is likely not yet done by the time we get here;
// we'll get here almost immediately, but the above completion handler is called when the asynchronous
// calculation is done.

马丁推测您正在计算曼德尔布罗特集。如果是这样,将每个点的计算分派到全局队列并不是一个好主意(因为这些全局队列将它们的块分派到工作线程,但这些工作线程非常有限)。

如果您想避免用完所有这些全局队列工作线程,一个简单的选择是采用async调用计算单个点的例程,然后将迭代所有复杂值的整个例程分派到后台线程:

DispatchQueue.global(qos: .userInitiated).async {
    for row in 0 ..< height {
        for column in 0 ..< width {
            let c = ...
            let m = self.mandelbrotValue(c)
            pixelBuffer[row * width + column] = self.color(for: m)
        }
    }

    let outputCGImage = context.makeImage()!

    DispatchQueue.main.async {
        completionHandler(NSImage(cgImage: outputCGImage, size: NSSize(width: width, height: height)))
    }
}

这解决了“将其从主线程中取出”和“不要用完工作线程”的问题,但现在我们已经从使用太多工作线程转变为仅使用一个工作线程,没有充分利用设备。我们确实希望并行执行尽可能多的计算(同时不耗尽工作线程)。

一种方法是,当做for循环进行复杂的计算,是使用dispatch_apply(现在称为concurrentPerform在斯威夫特 3)。这就像一个for循环,但它相对于彼此同时执行每个循环(但最后,等待所有这些并发循环完成)。为此,请更换外部for循环与concurrentPerform:

DispatchQueue.global(qos: .userInitiated).async {
    DispatchQueue.concurrentPerform(iterations: height) { row in
        for column in 0 ..< width {
            let c = ...
            let m = self.mandelbrotValue(c)
            pixelBuffer[row * width + column] = self.color(for: m)
        }
    }

    let outputCGImage = context.makeImage()!

    DispatchQueue.main.async {
        completionHandler(NSImage(cgImage: outputCGImage, size: NSSize(width: width, height: height)))
    }
}

The concurrentPerform(原名dispatch_apply)将同时执行该循环的各种迭代,但它会根据设备的功能自动优化并发线程的数量。在我的 MacBook Pro 上,这使得计算速度比简单的计算快 4.8 倍for环形。注意,我仍然将整个事情分派到全局队列(因为concurrentPerform同步运行,我们永远不想在主线程上执行缓慢的同步计算),但是concurrentPerform将并行运行计算。这是享受并发性的好方法for以不会耗尽 GCD 工作线程的方式进行循环。

mandelbrotset


顺便说一句,您提到您正在更新NSProgressIndicator。理想情况下,您希望在处理每个像素时对其进行更新,但如果这样做,UI 可能会积压,无法跟上所有这些更新。您最终会减慢最终结果的速度,以允许 UI 赶上所有这些进度指示器更新。

解决方案是将 UI 更新与进度更新分离。您希望后台计算在更新每个像素时通知您,但您希望更新进度指示器,每次都有效地说“好吧,更新进度,无论自上次检查以来计算了多少像素”。有一些繁琐的手动技术可以做到这一点,但 GCD 提供了一个非常优雅的解决方案,一个调度源,或者更具体地说,一个DispatchSourceUserDataAdd.

因此,定义调度源的属性和计数器来跟踪到目前为止已处理的像素数量:

let source = DispatchSource.makeUserDataAddSource(queue: .main)
var pixelsProcessed: UInt = 0

然后为调度源设置一个事件处理程序,该处理程序更新进度指示器:

source.setEventHandler() { [unowned self] in
    self.pixelsProcessed += self.source.data
    self.progressIndicator.doubleValue = Double(self.pixelsProcessed) / Double(width * height)
}
source.resume()

然后,当您处理像素时,您可以简单地add从后台线程到您的源:

DispatchQueue.concurrentPerform(iterations: height) { row in
    for column in 0 ..< width {
        let c = ...
        let m = self.mandelbrotValue(for: c)
        pixelBuffer[row * width + column] = self.color(for: m)
        self.source.add(data: 1)
    }
}

如果这样做,它将以尽可能高的频率更新 UI,但永远不会因更新队列而积压。调度源将合并这些add呼唤你。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

长周期区块应用 的相关文章

随机推荐

  • 羽毛调用自定义API方法

    我用如下内容定义我的 api class MyFeathersApi feathersClient any accountsAPI any productsAPI any constructor app var port number ap
  • Flask-RESTful 如何添加资源并传递非全局数据

    In the Flask RESTful示例应用程序发布在这里 the TODOS集合是一个全局变量 之后Todo Resource已登记 api add resource Todo todos
  • Django:限制OnetoMany关系中的关系数量

    您好 我希望将外键关系限制为特定数量 假设一支篮球队只能有 12 个人 class Team models Model teamName models CharField max length 20 teamColors models Ch
  • Logger slf4j 使用 {} 格式化而不是字符串连接的优点

    使用有什么好处吗 而不是字符串连接 slf4j 的一个例子 logger debug Temperature set to Old temperature was t oldT 代替 logger debug Temperature set
  • 为什么在nodejs的for循环中let比var慢?

    我写了一个非常简单的基准测试 console time var for var i 0 i lt 100000000 i console timeEnd var console time let for let i 0 i lt 10000
  • 垂直和水平中心图像

    我倾向于在我设计的每个网站上都这样做 但我还没有真正找到真正的好方法来做到这一点 公司通常会给我他们的徽标 当您进入该页面时 我将其置于屏幕中间 然后它会自动将您转发到主页 如果没有一堆表格和 div 我似乎找不到一种将图像居中于屏幕中间的
  • 如何更改 libgdx 中位图字体的 alpha

    我想制作一个消息框 当您接受时该消息框会消失 但我不知道如何更改字体的 alpha 这是我制作字体的方法 white new BitmapFont Gdx files internal data font whitefont fnt fal
  • 如何更改 Apache FOP Xalan XSLT 处理器?

    我正在使用 Apache FOP 1 1 我想将当前的 apache Xalan XSLT 处理器更改为其他处理器以支持 XSLT 2 0 目前它不支持 XSLT 2 0 请帮助我如何解决这个问题 谢谢 我得到了答案 Transformer
  • 将循环的每次迭代延迟一定时间

    JSFiddle http jsfiddle net KH8Gf 27 Code document ready function expand click function var qty qty val for var counter 0
  • sigma.js 不读取 JSON

    我有这个页面 div div
  • magento 显示类别中的产品数量错误

    我有一个奇怪的问题 似乎很多人在互联网上都有同样的问题 下图将定义我的问题 而且我的 magento 版本是 1 7 正如我所强调的 LEFT 表示该类别有 16 个产品 但实际上 类别产品 选项卡显示 15 个产品 我所有的分类都乱了 请
  • EWS 获取所有文件夹中未读电子邮件的计数

    我正在尝试从 Exchange 获取特定用户的未读电子邮件数量 我可以从收件箱获取电子邮件数量 如下所示 SearchFilter sf new SearchFilter SearchFilterCollection LogicalOper
  • sp_send_dbmail不会发送查询结果

    我已经尝试了每个该死的论坛建议的所有途径 但无济于事 需要发送已存储在表中的 SQLPERF logspace 结果 通过sp send dbmail给收件人 作业的第 2 步是发生故障的地方 请帮忙 EXEC msdb dbo sp se
  • Firebase 身份验证 iOS 12 无效令牌

    我有一个使用 Firebase 身份验证的应用程序 该身份验证使用电话短信验证和验证码 无 APNS 如果您使用的是 iOS 11 4 1 该应用程序可以在身份验证过程中正常运行 但是 如果您使用的是 iOS 12 则身份验证过程会导致无效
  • Iframe 中的会话在 Firefox 中工作,但在 Internet Explorer 中不工作

    我正在尝试让表单在 Internet Explorer 中工作 我发现当我在 Firefox 中提交此表单时 我可以启动一个会话并根据该会话将我的网络浏览器发送到正确的页面 然而 在 Internet Explorer 中 当我调试 SES
  • Android中如何设置按钮点击效果?

    在Android中 当我为按钮设置背景图像时 单击它时看不到任何效果 我需要在按钮上设置一些效果 以便用户可以识别出该按钮被单击 单击该按钮后 该按钮应变暗几秒钟 这个怎么做 这可以通过创建一个包含按钮状态列表的可绘制 xml 文件来实现
  • 箭头函数内的 async/await (Array#map/filter)

    我在此代码中遇到编译时错误 const someFunction async myArray gt return myArray map myValue gt return id my id myValue await service ge
  • 角度路由 - 重定向到外部站点?

    在 AngularJS 路由文件中 有一个选项otherwise路由 替换 404 routeProvider when otherwise redirectTo my path 有没有办法做到这一点 以便重定向到不在应用程序中的页面 我试
  • 保持终端焦点

    我有一个 python 脚本 它使用 selenium 来自动化网页 将焦点从需要用户输入的终端上移开 python 中是否有以编程方式将焦点切换回终端的方法 如果重要的话 我将在 Windows 7 上的 Windows 命令提示符中运行
  • 长周期区块应用

    我的应用程序中有以下循环 var maxIterations Int 0 func calculatePoint cn Complex gt Int let threshold Double 2 var z Complex init re