我可以从多个线程中绘制同一个CGContextRef吗? [英] Can I draw to the same CGContextRef from multiple threads?

查看:57
本文介绍了我可以从多个线程中绘制同一个CGContextRef吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个想要绘制很多形状的应用程序-圆,盒子,直线等。
数百万种。

I'm making an app where I want to draw a lot of shapes - circles, boxes, lines, etc. Millions of them.

为了测试其性能,我将这个简单的UIView组合在一起。请注意,这是应得的-我受到了此项目的启发。

To test the performance of this, I threw together this simple UIView. Note that credit is due - I got inspired by this project.

import UIKit

let qkeyString = "label" as NSString
var QKEY = qkeyString.UTF8String
let qvalString = "com.hanssjunnesson.Draw" as NSString
var QVAL = qvalString.UTF8String

public class RenderImageView: UIView {

    var bitmapContext: CGContext?

    let drawQueue: dispatch_queue_attr_t = {
        let q = dispatch_queue_create(QVAL, nil)
        dispatch_queue_set_specific(q, QKEY, &QVAL, nil)

        return q
    }()

    public override init() {
        super.init()

        render()
    }

    required public init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        render()
    }

    override required public init(frame: CGRect) {
        super.init(frame: frame)

        render()
    }

    public override func drawRect(rect: CGRect) {
        if let bitmapContext = self.bitmapContext {
            let context = UIGraphicsGetCurrentContext()
            let image = CGBitmapContextCreateImage(bitmapContext)
            CGContextDrawImage(context, self.bounds, image)
        }
    }

    private func render() {
        dispatch_async(drawQueue) {
            let startDate = NSDate()

            let bounds = self.bounds
            UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
            let context = UIGraphicsGetCurrentContext()
            self.bitmapContext = context

            CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
            CGContextFillRect(context, bounds)

            CGContextSetFillColorWithColor(context, UIColor(red: 0.15, green: 0.4, blue: 0.8, alpha: 1.0).CGColor)

            for i in 1...1000000 {
                CGContextFillEllipseInRect(context, bounds)
            }

            UIGraphicsEndImageContext()

            self.setNeedsDisplay()

            let benchmark = startDate.timeIntervalSinceNow
            println("Rendering took: \(-benchmark*1000) Ms")
        }
    }
}

这很好用。在我的iOS模拟器上,花一分钟多一点的时间可以互相绘制一百万个圆。

This works just fine. On my iOS simulator, it takes little over a minute to draw a million circles on top of each other.

我想加快速度,因此我尝试绘制

I wanted to speed this up, so I tried drawing to the bitmap context from multiple threads.

let group = dispatch_group_create()
for i in 1...100 {
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
        dispatch_group_enter(group)

        CGContextFillEllipseInRect(context, bounds)

        dispatch_group_leave(group)
    }
}

dispatch_group_wait(group, DISPATCH_TIME_FOREVER)

但是,这没有用。调用 CGContextFillEllipseInRect(context,bounds)时得到 EXC_BAD_ACCESS

This did not work, however. I get an EXC_BAD_ACCESS when calling CGContextFillEllipseInRect(context, bounds).

在后台线程中绘制到 CGContext 看起来很好,只要它是创建它的相同线程即可。

Drawing to a CGContext in a background thread seems fine as long as it's the same thread that created it.

有人知道让它正常工作的方法吗?

Anyone know of a way of getting this to work?

推荐答案

1)您实际上并没有在等待该小组您创建完成的-在执行任何块之前,将在该代码中调用 dispatch_group_wait ,因此其中的enter / leave调用将没有任何作用影响。改为使用 dispatch_group_async (见下文)。

1) You aren't actually waiting for the group you created to finish -- dispatch_group_wait is going to be called in that code before any of the blocks have been executed, so the enter / leave calls inside them won't have any effect. Use dispatch_group_async instead (see below).

2)您无法绘制到 CGContext 同时来自两个不同的线程-如果在绘图循环中添加println,则可以看到此内容。它将工作几次,并得到不同的结果,但是最终您将遇到一个错误。

2) You can't draw to a CGContext from two different threads at the same time -- you can see this if you add a println inside your drawing loop. It will work a few times, with varying results, but eventually you'll be end up with an error.

let group = dispatch_group_create()
for i in 1...10 {
    dispatch_group_async(group, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
        for j in 1...100 {
            println("i:\(i), j:\(j)")
            CGContextFillEllipseInRect(context, bounds)
        }
    }
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)

示例输出:

iiii::::4123,,,,    jjjj::::1111



ii:1, j:2
:5, j:1
i:6, j:1
EXC_BAD_ACCESS

唯一的解决方案是跳回单个线程进行绘图,但这使您尝试以任何方式进行的操作都失败了。如果必须进行大量计算来决定要绘制的内容,则可能会在单独的线程上发生,但是绘制到 CGContext 本身并不安全。

The only solution to this is to jump back on a single thread for drawing, but that defeats what you were trying to do any way. If you have to do lots of calculations to decide what to draw, that could happen on separate threads, but drawing to CGContext itself isn't thread safe.

这篇关于我可以从多个线程中绘制同一个CGContextRef吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆