dispatch_sync对主队列上的dispatch_async [英] dispatch_sync vs. dispatch_async on main queue

查看:164
本文介绍了dispatch_sync对主队列上的dispatch_async的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

与我一起,这将要采取一些解释。我有一个类似下面的函数。



上下文:aProject是一个名为LPProject的核心数据实体,名为memberFiles,包含另一个核心数据实体称为LPFile。每个LPFile表示磁盘上的文件,我们要做的是打开每个文件并解析其文本,查找指向OTHER文件的@import语句。如果我们找到@import语句,我们要定位它们指向的文件,然后通过向表示第一个文件的核心数据实体添加关系将该文件链接到该文件。因为所有这些都需要一些时间在大型文件上,我们将使用GCD在主线程上执行。

   - void)establishImportLinksForFilesInProject:(LPProject *)aProject {
dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
for(LPFile * fileToCheck in aProject.memberFiles){
if(//满足一些条件){
dispatch_async(taskQ,^ {
//这里,扫描@import语句
//当我们找到一个有效的文件时,我们将导入的文件的整个路径放入一个名为'verifiedImports'的数组中

//回到主线程和更新模型(Core Data不是线程安全的)
dispatch_sync(dispatch_get_main_queue(),^ {

NSLog(@Got to main thread。
$ b(NSString * import in verifiedImports){
//将关系添加到Core Data LPFile实体
}
}); // end block
}); // end block
}
}
}

现在,这里的东西变得奇怪:



这段代码工作,但我看到一个奇怪的问题。如果我在一个有几个文件(约20)的LPProject上运行它,它运行完美。但是,如果我在具有更多文件(例如,60-70)的LPProject上运行它,它会正确运行。我们从不回到主线程, NSLog(@得到主线程); 从不出现,应用程序挂起。但是,(这就是事情变得很奇怪)---如果我在小项目上运行代码FIRST,然后在大项目上运行它,一切工作完美。



如果我把第二个调度行改成这样:

p>

  dispatch_async(dispatch_get_main_queue(),^ {

(也就是说,使用 async 而不是 sync 主队列),一切工作的所有时间。完美无论一个项目中的文件数量!



我有一个失去解释这个行为。任何帮助

解决方案

这是与磁盘I / O和GCD相关的常见问题。基本上,GCD可能为每个文件生成一个线程,并且在某一时刻你有太多的线程,系统在合理的时间内服务。



每次调用dispatch_async()并在该块中尝试任何I / O(例如,它看起来像是在这里读取一些文件),可能是该代码块正在执行的线程块(由OS暂停),同时它等待从文件系统读取数据。 GCD的工作方式是这样的,当它看到其中一个工作线程被阻塞在I / O上,你仍然要求它同时做更多的工作,它只会产生一个新的工作线程。因此,如果你试图在一个并发队列中打开50个文件,很可能会导致GCD产生〜50个线程。



这是太多的线程系统有意义的服务,你最终会饿死你的主线程的CPU。



解决这个问题的方法是使用一个串行队列,而不是一个并发队列您的基于文件的操作。这很容易做到。您将需要创建一个串行队列并将其作为一个ivar存储在对象中,因此您最终不会创建多个连续队列。所以删除此调用:



dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);



在您的init方法中添加:



taskQ = dispatch_queue_create(com.yourcompany.yourMeaningfulLabel,DISPATCH_QUEUE_SERIAL) ;



在您的dealloc方法中添加:



dispatch_release(taskQ);



并在类声明中将它添加为一个ivar:


$ b b

dispatch_queue_t taskQ;


Bear with me, this is going to take some explaining. I have a function that looks like the one below.

Context: "aProject" is a Core Data entity named LPProject with an array named 'memberFiles' that contains instances of another Core Data entity called LPFile. Each LPFile represents a file on disk and what we want to do is open each of those files and parse its text, looking for @import statements that point to OTHER files. If we find @import statements, we want to locate the file they point to and then 'link' that file to this one by adding a relationship to the core data entity that represents the first file. Since all of that can take some time on large files, we'll do it off the main thread using GCD.

- (void) establishImportLinksForFilesInProject:(LPProject *)aProject {
    dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     for (LPFile *fileToCheck in aProject.memberFiles) {
         if (//Some condition is met) {
            dispatch_async(taskQ, ^{
                // Here, we do the scanning for @import statements. 
                // When we find a valid one, we put the whole path to the imported file into an array called 'verifiedImports'. 

                // go back to the main thread and update the model (Core Data is not thread-safe.)
                dispatch_sync(dispatch_get_main_queue(), ^{

                    NSLog(@"Got to main thread.");

                    for (NSString *import in verifiedImports) {  
                            // Add the relationship to Core Data LPFile entity.
                    }
                });//end block
            });//end block
        }
    }
}

Now, here's where things get weird:

This code works, but I'm seeing an odd problem. If I run it on an LPProject that has a few files (about 20), it runs perfectly. However, if I run it on an LPProject that has more files (say, 60-70), it does NOT run correctly. We never get back to the main thread, the NSLog(@"got to main thread"); never appears and the app hangs. BUT, (and this is where things get REALLY weird) --- if I run the code on the small project FIRST and THEN run it on the large project, everything works perfectly. It's ONLY when I run the code on the large project first that the trouble shows up.

And here's the kicker, if I change the second dispatch line to this:

dispatch_async(dispatch_get_main_queue(), ^{

(That is, use async instead of sync to dispatch the block to the main queue), everything works all the time. Perfectly. Regardless of the number of files in a project!

I'm at a loss to explain this behavior. Any help or tips on what to test next would be appreciated.

解决方案

This is a common issue related to disk I/O and GCD. Basically, GCD is probably spawning one thread for each file, and at a certain point you've got too many threads for the system to service in a reasonable amount of time.

Every time you call dispatch_async() and in that block you attempt to to any I/O (for example, it looks like you're reading some files here), it's likely that the thread in which that block of code is executing will block (get paused by the OS) while it waits for the data to be read from the filesystem. The way GCD works is such that when it sees that one of its worker threads is blocked on I/O and you're still asking it to do more work concurrently, it'll just spawn a new worker thread. Thus if you try to open 50 files on a concurrent queue, it's likely that you'll end up causing GCD to spawn ~50 threads.

This is too many threads for the system to meaningfully service, and you end up starving your main thread for CPU.

The way to fix this is to use a serial queue instead of a concurrent queue to do your file-based operations. It's easy to do. You'll want to create a serial queue and store it as an ivar in your object so you don't end up creating multiple serial queues. So remove this call:

dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

Add this in your init method:

taskQ = dispatch_queue_create("com.yourcompany.yourMeaningfulLabel", DISPATCH_QUEUE_SERIAL);

Add this in your dealloc method:

dispatch_release(taskQ);

And add this as an ivar in your class declaration:

dispatch_queue_t taskQ;

这篇关于dispatch_sync对主队列上的dispatch_async的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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