Skip to content

Latest commit

 

History

History
103 lines (71 loc) · 3.71 KB

718 - GCD.md

File metadata and controls

103 lines (71 loc) · 3.71 KB

Building Responsive and Efficient Apps with GCD (718)

Quality of Service (QoS)

QoS Classes

Allows you to categorize the type of work you’re doing in order to allow GCD to better prioritize the work.

  • User Interactive: main thread, animations
  • User Initiated: Immediate results
  • Utility: Long-running tasks, shouldn’t prevent user progress
  • Background: Not user visible

See writing efficient apps talk from 2014 for more info on effectively scheduling background work

On MacBook One and iOS devices

Since thermal dissipation is constant (no fans), throughput is managed based on QoS classes in order to control heat.

Design Patterns with GCD + QoS

See power, performance and Diagnostics: Whats new in GCD + XPC WWDC 2014

dispatch_async() automatically propagates the QoS from the calling thread, though it will translate User Interactive to User Initiated to avoid assigning that priority to non mainthreads.

Captured at time of block submission, translatse user interactive to user initiated. Used if destination queue does not have a QoS and does not lower the QoS (ex dispatch_async back to the main thread)

Manually set QoS for a block

b = dispatch_block_create_with_qos_class(…)
dispatch_async(q, b)

QoS is captured at block object creation. Can be manually capture the current QoS by DISPATCH_BLOCK_ASSIGN_CURRENT.

DISPATCH_BLOCK_DETACHED flag tells OS that the block has no relation to current execution flow, won’t capture activity id or QoS scope.

DISPATCH_BLOCK_FORCE_QOS_CLASS flag tell queue to override QoS for this block

Asynchronous Priority Inversion

Queue is already running a block lower priority than one is queued. QoS is raised until the higher priority block is run, when you use dispatch_sync() and dispatch_block_wait() or pthread_mutex_lock().

Using serial queues as locks

dispach_queue_create(“com.example”, DISPATCH_QUEUE_SERIAL); will use the QOS of the calling thread, can lead to priority inversion.

Queues, Threads, and Run Loops

dispatch_async(q, ^{ 
    [self performSelector:… withObject:nil afterDelay:1]
})

Won’t get called, because the thread is ephemeral and it doesn’t have a running run loop.

Run loop

  • Bound to a thread
  • Gets delegate method callbacks
  • AUtorelease pool pots after each iteration
  • Can be used reentrantly
  • [self performSelector:… withObject:… afterDelay:…]

Serial Queue

  • Uses ephemeral threads
  • Blcok callbacks
  • Autorelease pool pops when thread is idle Not guaranteed to happen if queue is busy!
  • Will deadlock if used reentrantly
  • dispatch_after()

Waiting

When a thread is waiting (blocked on IO/lock), GCD will spawn additional threads to tackle the next queued work while the first is waiting. Need to be careful about thread explosion, and deadlock.

Avoiding Thread Explosion

  • Always good advice: use asynchronous APIs, especially for I/O
  • Use serial queues where relevant
  • Use NSOperationQueues with concurrency limits
  • Don’t generate unlimited work

Avoiding deadlock

Don’t mix dispatch_async/dispatch_sync when you don’t have to, and especially be careful from the main thread.

// Bad, can experience deadlock/
for (int i = 0; i < 999; i++) {
    dispatch_async(q, ^{});
}
dispatch_barrier_sync(q)

// Better
dispatch_apply…

dispach_semaphore can also be used to ensure that only n number of blocks are queued (block submitting code)

Reading crash logs

Manager thread (_dispatch_manager_thrd), can basically ignore responsible for GCD thread pool

__workq_kernreturn is an idle gcd thread

_dispach_client_callout gcd thread calling out to your block

mack_msg_trap main thread idle

__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ main thread active