objective c - dispatch_sync vs. dispatch_async on main queue -
bear me, going take explaining. have function looks 1 below.
context: "aproject" core data entity named lpproject array named 'memberfiles' contains instances of core data entity called lpfile. each lpfile represents file on disk , want open each of files , parse text, looking @import statements point other files. if find @import statements, want locate file point , 'link' file 1 adding relationship core data entity represents first file. since of can take time on large files, we'll off main thread using gcd.
- (void) establishimportlinksforfilesinproject:(lpproject *)aproject { dispatch_queue_t taskq = dispatch_get_global_queue(dispatch_queue_priority_default, 0); (lpfile *filetocheck in aproject.memberfiles) { if (//some condition met) { dispatch_async(taskq, ^{ // here, scanning @import statements. // when find valid one, put whole path imported file array called 'verifiedimports'. // go main thread , update model (core data not thread-safe.) dispatch_sync(dispatch_get_main_queue(), ^{ nslog(@"got main thread."); (nsstring *import in verifiedimports) { // add relationship core data lpfile entity. } });//end block });//end block } } }
now, here's things weird:
this code works, i'm seeing odd problem. if run on lpproject has few files (about 20), runs perfectly. however, if run on lpproject has more files (say, 60-70), not run correctly. never main thread, nslog(@"got main thread");
never appears , app hangs. but, (and things weird) --- if run code on small project first , run on large project, works perfectly. it's when run code on large project first trouble shows up.
and here's kicker, if change second dispatch line this:
dispatch_async(dispatch_get_main_queue(), ^{
(that is, use async
instead of sync
dispatch block main queue), works time. perfectly. regardless of number of files in project!
i'm @ loss explain behavior. or tips on test next appreciated.
this common issue related disk i/o , gcd. basically, gcd spawning 1 thread each file, , @ point you've got many threads system service in reasonable amount of time.
every time call dispatch_async() , in block attempt to i/o (for example, looks you're reading files here), it's thread in block of code executing block (get paused os) while waits data read filesystem. way gcd works such when sees 1 of worker threads blocked on i/o , you're still asking more work concurrently, it'll spawn new worker thread. if try open 50 files on concurrent queue, it's you'll end causing gcd spawn ~50 threads.
this many threads system meaningfully service, , end starving main thread cpu.
the way fix use serial queue instead of concurrent queue file-based operations. it's easy do. you'll want create serial queue , store ivar in object don't end creating multiple serial queues. remove call:
dispatch_queue_t taskq = dispatch_get_global_queue(dispatch_queue_priority_default, 0);
add in init method:
taskq = dispatch_queue_create("com.yourcompany.yourmeaningfullabel", dispatch_queue_serial);
add in dealloc method:
dispatch_release(taskq);
and add ivar in class declaration:
dispatch_queue_t taskq;
Comments
Post a Comment