在中央车站调度系统中调度同步

有人能够用非常清晰的用例解释 GCDdispatch_sync的用途吗?我不明白我为什么要用这个。

谢谢!

50916 次浏览

当您想执行一个块并等待结果时,可以使用它。

这方面的一个例子是使用调度队列而不是锁进行同步的模式。例如,假设您有一个共享的 NSMutableArray a,其访问由分派队列 q介导。后台线程可能附加到数组(异步) ,而前台线程正在(同步地)提取第一个项:

NSMutableArray *a = [[NSMutableArray alloc] init];
// All access to `a` is via this dispatch queue!
dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", NULL);


dispatch_async(q, ^{ [a addObject:something]; }); // append to array, non-blocking


__block Something *first = nil;            // "__block" to make results from block available
dispatch_sync(q, ^{                        // note that these 3 statements...
if ([a count] > 0) {               // ...are all executed together...
first = [a objectAtIndex:0];  // ...as part of a single block...
[a removeObjectAtIndex:0];    // ...to ensure consistent results
}
});

从语义上讲,send _ sync 等同于传统的互斥锁。

dispatch_sync(queue, ^{
//access shared resource
});

工作原理与

pthread_mutex_lock(&lock);
//access shared resource
pthread_mutex_unlock(&lock);

这里有一个半现实的例子。您需要并行分析2000个 zip 文件。但是 zip 库不是线程安全的。因此,所有触及 zip 库的工作都进入 unzipQueue队列。(示例是在 Ruby 中,但所有调用都直接映射到 C 库。例如,“应用”映射到 派遣 _ 申请(3))

#!/usr/bin/env macruby -w


require 'rubygems'
require 'zip/zipfilesystem'


@unzipQueue = Dispatch::Queue.new('ch.unibe.niko.unzipQueue')
def extractFile(n)
@unzipQueue.sync do
Zip::ZipFile.open("Quelltext.zip") {   |zipfile|
sourceCode = zipfile.file.read("graph.php")
}
end
end


Dispatch::Queue.concurrent.apply(2000) do |i|
puts i if i % 200 == 0
extractFile(i)
end

先了解它的兄弟 dispatch_async

//Do something
dispatch_async(queue, ^{
//Do something else
});
//Do More Stuff

使用 dispatch_async创建新线程。执行此操作时,当前线程将不会停止。这意味着 //Do More Stuff可能在 //Do something else完成之前执行

如果希望当前线程停止,会发生什么?

您根本不使用调度,只是正常地编写代码

//Do something
//Do something else
//Do More Stuff

现在,假设您想在 不一样线程上执行某些操作,然后等待,并确保完成 连续

这样做有很多原因。例如,UI 更新是在主线程上完成的。

这就是使用 dispatch_sync的地方

//Do something
dispatch_sync(queue, ^{
//Do something else
});
//Do More Stuff

在这里,你得到了 //Do something //Do something else//Do More stuff连续完成,即使 //Do something else是在不同的线程上完成的。

通常,当人们使用不同的线程时,整个目的就是让某些事情不用等待就可以执行。假设您希望下载大量数据,但是希望保持 UI 平滑。

因此,很少使用 patch _ sync。但它就在那里。我个人从没用过。为什么不要求一些示例代码或项目确实使用分派 _ sync。

如果你想要一些实用的例子,看看我的这个问题:

我如何解决这种偶尔发生的僵局?

我通过确保在主线程上创建 main management ObjectContext 来解决这个问题。这个过程非常快,我不介意等待。不等待意味着我将不得不处理许多并发问题。

因为需要在主线程上执行一些代码,所以需要调度 _ sync,主线程与执行代码的线程是不同的。

所以基本上如果你想让代码 1.照常进行。你不想担心比赛条件。您希望在继续之前确保代码已经完成。 2. 用不同的线程完成

使用调度同步。

如果违反了1,就使用分派 _ 异步。如果违反了2,就像平常一样编写代码。

到目前为止,我只做这一次,也就是说,当需要在主线程上完成某些事情时。

密码是这样的:

+(NSManagedObjectContext *)managedObjectContext {




NSThread *thread = [NSThread currentThread];
//BadgerNewAppDelegate *delegate = [BNUtilitiesQuick appDelegate];
//NSManagedObjectContext *moc = delegate.managedObjectContext;


if ([thread isMainThread]) {
//NSManagedObjectContext *moc = [self managedObjectContextMainThread];
return [self managedObjectContextMainThread];
}
else{
dispatch_sync(dispatch_get_main_queue(),^{
[self managedObjectContextMainThread];//Access it once to make sure it's there
});
}


// a key to cache the context for the given thread
NSMutableDictionary *managedObjectContexts =[self thread].managedObjectContexts;


@synchronized(self)
{
if ([managedObjectContexts objectForKey:[self threadKey]] == nil ) {
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
threadContext.parentContext = [self managedObjectContextMainThread];
//threadContext.persistentStoreCoordinator= [self persistentStoreCoordinator]; //moc.persistentStoreCoordinator;//  [moc persistentStoreCoordinator];
threadContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[managedObjectContexts setObject:threadContext forKey:[self threadKey]];
}
}




return [managedObjectContexts objectForKey:[self threadKey]];
}

我曾经在异步调度中使用调度同步来将 UI 更改信号发送回主线程。

我的异步块只有一点保留,我知道主线程知道 UI 的变化,并将采取行动。通常在代码处理块中使用这种方法,这需要一些 CPU 时间,但是我仍然希望在该块中操作 UI 更改。在异步块中操作 UI 更改是无用的,因为我相信 UI 是在主线程上运行的。将它们作为辅助异步块或自我委托进行操作,会导致用户界面在几秒钟后才看到它们,而且看起来有些迟缓。

示例块:

dispatch_queue_t myQueue = dispatch_queue_create("my.dispatch.q", 0);
dispatch_async(myQueue,
^{


//  Do some nasty CPU intensive processing, load file whatever


if (somecondition in the nasty CPU processing stuff)
{
//  Do stuff
dispatch_sync(dispatch_get_main_queue(),^{/* Do Stuff that affects UI Here */});
}


});

David Gelhar 没有说明他的示例将只是因为他悄悄地创建了串行队列(调度 _ queue _ create 中传递了等于 DISPATCH _ QUEUE _ SERIAL 的 NULL)。

如果您希望创建并发队列(以获得所有多线程能力) ,他的代码将导致崩溃,因为 NSArray 变异(addObject:)在变异期间(RemoveObjectAtIndex:) ,甚至错误访问(NSArray 范围超出界限)。在这种情况下,我们应该在两个块运行时使用阻塞来确保对 NSArray 的独占访问。它不仅在运行 NSArray 时排除了对它的所有其他写操作,而且还排除了所有其他读操作,从而保证了修改的安全性。

并发队列的示例应该是这样的:

NSMutableArray *a = [[NSMutableArray alloc] init];
// All access to `a` is via this concurrent dispatch queue!
dispatch_queue_t q = dispatch_queue_create("com.foo.samplequeue", DISPATCH_QUEUE_CONCURRENT);


// append to array concurrently but safely and don't wait for block completion
dispatch_barrier_async(q, ^{ [a addObject:something]; });


__block Something *first = nil;
// pop 'Something first' from array concurrently and safely but wait for block completion...
dispatch_barrier_sync(q, ^{
if ([a count] > 0) {
first = [a objectAtIndex:0];
[a removeObjectAtIndex:0];
}
});
// ... then here you get your 'first = [a objectAtIndex:0];' due to synchronised dispatch.
// If you use async instead of sync here, then first will be nil.

Send _ sync 主要用于分派异步块内部,在主线程上执行一些操作(比如 update ui)。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Update UI in main thread
dispatch_sync(dispatch_get_main_queue(), ^{
self.view.backgroundColor = color;
});
});