5

在我的代码中,我进行了子类化NSView,在它的drawRect方法中,我产生了三个线程来执行绘图。

-(void)drawRect:(NSRect)dirtyRect
{
    [[self window] setAllowsConcurrentViewDrawing:YES];
    [self setCanDrawConcurrently:YES];

    [NSThread detachNewThreadSelector:@selector(DrawText) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(DrawRectangle) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(DrawGradient) toTarget:self withObject:nil];

    //Wherease these functions DrawText, DrawRectangle and DrawGradient performs their task as suggested by name.

    //In DrawText, DrawRectangle, and DrawGradient lockFocus and unlockFocus is being
    // used for multithreaded drawing.

}

当我从 Xcode 运行相同的程序时,它运行良好。输出如下所示。 单线程

但是当我从外部运行它时,出现了问题,输出如下所示。 多线程

首先,我想知道从辅助线程中提取是否正确?或者从辅助线程中提取的另一种方法是什么?

这个问题背后的原因是什么?

4

3 回答 3

7

Ken Aspeslagh 关于从辅助线程中绘制有点不正确(他是正确的,这通常是一个坏主意)。从我对您的代码的了解来看,您没有在辅助线程上绘图的良好用例。你能解释一下你为什么要这样做吗?

您自己已经发现了setCanDrawConcurrently:哪些明确谈论drawRect:从后台线程调用。请注意,视图窗口必须allowsConcurrentViewDrawing设置为 YES 才能工作(这是默认设置)。

Apples 自己的Cocoa Drawing Guide有一个关于从辅助线程绘图的部分。我强调了一些我认为与您相关的部分。

Application Kit 为每个窗口和线程组合维护一个独特的图形上下文。因为每个线程对给定窗口都有自己的图形上下文对象,所以可以使用辅助线程来绘制到该窗口。但是,有一些警告。

在 Windows 的正常更新周期中,所有绘图请求都会发送到应用程序的主线程进行处理。当用户事件触发用户界面的更改时,就会发生正常的更新周期。在这种情况下,您可以从应用程序的主线程调用 setNeedsDisplay: 或 setNeedsDisplayInRect: 方法(或显示方法系列),以使需要重绘的视图部分无效。您不应从任何辅助线程调用这些方法。

如果要从辅助线程更新窗口或视图,则必须手动将焦点锁定在窗口或视图上并自己启动绘图。锁定焦点为该窗口的图形上下文配置绘图环境。锁定后,您可以配置绘图环境,像往常一样发出绘图命令,然后将图形上下文的内容刷新到窗口缓冲区。

为了定期在辅助线程上绘图,您必须自己通知线程。发送常规通知的最简单方法是使用 NSTimer 或 NSAnimation 对象。有关如何为内容制作动画的更多信息,请参阅“高级绘图技术”。</p>

可可线程编程指南也这样说:

如果要使用线程绘制到视图,请将所有绘制代码​​括在 NSView 的 lockFocusIfCanDraw 和 unlockFocus 方法之间

顺便说一句,GCD 块调用可能是比NSThread.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    // you can put each of these calls in their own queue if you want
    [self DrawText];
    [self DrawRectangle];
    [self DrawGradient];
});

但是,这可能与您的问题无关;我提到它只是因为我认为使用 GCD 队列会更好地为您服务。

于 2013-11-13T17:09:18.350 回答
2

您应该只从主线程绘制到屏幕上。

编辑:这显然真的很复杂,所以你最好从主线程绘制到屏幕上。;)

如果您需要渲染需要花费太多时间以避免阻塞主线程的内容,请考虑使用线程将绘图绘制到屏幕外上下文,然后将该上下文复制到主线程上的屏幕。

于 2013-11-13T16:12:28.950 回答
1

NSGraphicsContext Restriction我在Thread guide读到过。

在这里,我找到了以下行:

如果您从辅助线程进行任何绘图,则必须手动刷新绘图调用。Cocoa 不会使用从辅助线程绘制的内容自动更新视图,因此您需要在完成绘制时调用 NSGraphicsContext 的 flushGraphics 方法。如果您的应用程序仅从主线程绘制内容,则不需要刷新绘图调用。

调用后flushGraphics,它工作正常。

于 2013-11-15T09:29:40.440 回答