在NSView中绘制多个线程

时间:2013-11-13 12:56:28

标签: objective-c multithreading macos cocoa nsview

在我的代码中,我将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运行相同的程序时,运行正常。输出如下所示。 Single threaded

但是当我从外面运行它时,有问题,输出如下所示。 Multithreaded

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

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

3 个答案:

答案 0 :(得分:7)

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

您自己已经发现setCanDrawConcurrently:明确谈到从后台线程调用drawRect:。请注意,视图窗口必须将allowsConcurrentViewDrawing设置为YES才能使其生效(这是默认设置)。

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

  

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

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

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

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

The Cocoa Threading Programming Guide也说了这个:

  

如果要使用线程绘制视图,请将所有绘图代码括在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队列。

答案 1 :(得分:2)

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

编辑:显然非常复杂,所以最好从主线程中抽取到屏幕。 ;)

如果你需要渲染一些花费太多时间来避免阻塞主线程的东西,可以考虑使用线程来绘制到屏幕外的上下文,然后将该上下文复制到主线程的屏幕上。

答案 2 :(得分:1)

我在Thread guide了解了NSGraphicsContext Restriction

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

  

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

致电flushGraphics后,工作正常。