Multithreading 我可以使用Grand Central dispatch将任务分派到OSX DisplayLink线程吗?
在OSX下的OpenGL应用程序中,渲染代码通常在DisplayLink线程上运行,该线程与主线程分离 在后台执行任务(例如加载GL资源)时,同步线程非常重要,因此渲染线程不会尝试从后台线程正在积极更改的模型中绘制 在主线程上进行渲染时,我一直在使用GCD将后台任务的关键部分分派到主分派队列,如下所示:Multithreading 我可以使用Grand Central dispatch将任务分派到OSX DisplayLink线程吗?,multithreading,macos,opengl,grand-central-dispatch,Multithreading,Macos,Opengl,Grand Central Dispatch,在OSX下的OpenGL应用程序中,渲染代码通常在DisplayLink线程上运行,该线程与主线程分离 在后台执行任务(例如加载GL资源)时,同步线程非常重要,因此渲染线程不会尝试从后台线程正在积极更改的模型中绘制 在主线程上进行渲染时,我一直在使用GCD将后台任务的关键部分分派到主分派队列,如下所示: dispatch_async(dispatch_get_main_queue(), ^{ [self doCriticalThing]; }); 但是,当在DisplayLink线程上进行渲染
dispatch_async(dispatch_get_main_queue(), ^{ [self doCriticalThing]; });
但是,当在DisplayLink线程上进行渲染时,这不起作用,因为毫不奇怪,当DisplayLink尝试渲染时,在尝试运行关键任务的主线程之间会出现线程冲突
是否可以使用GCD将任务分派到DisplayLink线程而不是主线程
或者我是否需要恢复到使用以下内容:
performSelector:onThread:withObject:waitUntilDone:
要将任务直接分配给DisplayLink线程?似乎没有任何内置API来实现这一点。不过,烹调起来相当简单。可能是这样的:
static CVReturn DispatchDisplayLinkOneshotCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext);
void dispatch_async_displaylink(CVDisplayLinkRef dl, dispatch_block_t block)
{
if (!block || !dl)
return;
CVDisplayLinkRef privateDisplayLink = nil;
if (kCVReturnSuccess != CVDisplayLinkCreateWithCGDisplay(CVDisplayLinkGetCurrentCGDisplay(dl), &privateDisplayLink) || !privateDisplayLink)
{
NSLog(@"Couldn't create display link for dispatch");
return;
}
CVDisplayLinkSetOutputCallback(privateDisplayLink, DispatchDisplayLinkOneshotCallback, (void*)CFBridgingRetain([block copy]));
CVDisplayLinkStart(privateDisplayLink);
}
static CVReturn DispatchDisplayLinkOneshotCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
{
dispatch_block_t block = (dispatch_block_t)CFBridgingRelease(displayLinkContext);
block();
CVDisplayLinkStop(displayLink);
CVDisplayLinkSetOutputCallback(displayLink, NULL, NULL);
CVDisplayLinkRelease(displayLink);
return kCVReturnSuccess;
}
CVDisplayLinkRef dl = nil;
if (kCVReturnSuccess != CVDisplayLinkCreateWithActiveCGDisplays(&dl))
{
NSLog(@"Problem");
}
MYDisplayLinkDispatcher* dld = [[MYDisplayLinkDispatcher alloc] initWithDisplayLink: dl];
for (NSUInteger i = 0; i < 100; i++)
{
[dld performBlock:^{
NSLog(@"Whee! Hello from the display link thread! i: %@", @(i));
}];
}
我确实注意到,根据经验,使用这种方法排队的块的最终执行顺序没有得到维护,因此如果这对您很重要,您可能会希望采用不同的方法,但是如果您只想“在显示链接回调上调用此块”,那么应该这样做
另外,FWIW,从技术上讲,这种方法并不是在你传递给它的确切的CVDisplayLink
的回调上执行,而是在为显示查询该回调,然后创建一个新的“一次性”CVDisplayLink
,因此如果你需要强有力的保证,你需要使用确切的CVDisplayLink
回调,同样,您需要在这周围添加一些额外的机器
-performSelector:onThread:withObject:waituntldone:
是基于runloop的,而且CVDisplayLink
线程似乎没有runloop(它们有一些东西,但看起来不像CFRunLoop
)编辑:显然这毕竟是可行的。不知道这是怎么回事,但这是一个选择
编辑:好的,我忍不住。下面是另一个示例,它维护顺序并使用您在初始化时传递给它的特定CVDisplayLink
。如果您有一个也需要被调用的“主”回调,那么您必须在初始化时将其(及其上下文)传入(因为一旦设置了CVDisplayLink
,就没有API可以从现有回调中提取,我们需要设置一个回调来服务队列。)
然后要使用它,您可以执行以下操作:
static CVReturn DispatchDisplayLinkOneshotCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext);
void dispatch_async_displaylink(CVDisplayLinkRef dl, dispatch_block_t block)
{
if (!block || !dl)
return;
CVDisplayLinkRef privateDisplayLink = nil;
if (kCVReturnSuccess != CVDisplayLinkCreateWithCGDisplay(CVDisplayLinkGetCurrentCGDisplay(dl), &privateDisplayLink) || !privateDisplayLink)
{
NSLog(@"Couldn't create display link for dispatch");
return;
}
CVDisplayLinkSetOutputCallback(privateDisplayLink, DispatchDisplayLinkOneshotCallback, (void*)CFBridgingRetain([block copy]));
CVDisplayLinkStart(privateDisplayLink);
}
static CVReturn DispatchDisplayLinkOneshotCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
{
dispatch_block_t block = (dispatch_block_t)CFBridgingRelease(displayLinkContext);
block();
CVDisplayLinkStop(displayLink);
CVDisplayLinkSetOutputCallback(displayLink, NULL, NULL);
CVDisplayLinkRelease(displayLink);
return kCVReturnSuccess;
}
CVDisplayLinkRef dl = nil;
if (kCVReturnSuccess != CVDisplayLinkCreateWithActiveCGDisplays(&dl))
{
NSLog(@"Problem");
}
MYDisplayLinkDispatcher* dld = [[MYDisplayLinkDispatcher alloc] initWithDisplayLink: dl];
for (NSUInteger i = 0; i < 100; i++)
{
[dld performBlock:^{
NSLog(@"Whee! Hello from the display link thread! i: %@", @(i));
}];
}
CVDisplayLinkRef dl=nil;
if(kCVReturnSuccess!=CVDisplayLinkCreateWithActiveCGDisplays(&dl))
{
NSLog(“问题”);
}
MYDisplayLinkDispatcher*dld=[[MYDisplayLinkDispatcher alloc]initWithDisplayLink:dl];
对于(整数i=0;i<100;i++)
{
[dld性能锁:^{
NSLog(@“Whee!你好,来自显示链接线程!i:%@,@(i));
}];
}
感谢您深入了解这一点!我很好奇为什么你说CVDisplayLink没有运行循环。我最后尝试了performSelector:。。。方法如果我在回调中放置断点,它似乎正在CVDisplayLinkThread上执行,nsrunlop运行模式:。。。出现在堆栈的回溯跟踪中。我错过什么了吗?哇!那么,我想我错了。我在回调的回溯中没有看到任何“常见的嫌疑犯”,所以我假设没有运行循环。可能runloop是按需创建的,或者从属于CVDisplayLink线程主例程。什么都管用,嗯?