This is the 20th day of my participation in the August More Text Challenge

review

In the last blog has made a GCD dispatch group introduction and example application, as well as the underlying source analysis, so this blog will be on the event source dispatch_source analysis!

Multithreading in iOS (I) – Processes and threads

IOS low-level exploration of multithreading (II) – Threads and locks

IOS low-level exploration of multithreading (3) – GCD

IOS low-level exploration of multithreading (4) – GCD queue

IOS bottom exploration of multithreading (five) – GCD different queue source analysis

IOS bottom exploration of multithreading (six) – GCD source analysis (sync function, async asynchronous function)

IOS low-level exploration of multithreading (seven) – GCD source analysis (cause of deadlock)

IOS bottom exploration of multithreading (eight) – GCD source analysis (function synchronization, asynchrony, singleton)

IOS bottom exploration of multithreading (9) – GCD source analysis (dispatch_barrier_sync)

Multithreading in iOS (10) — GCD source code Analysis (Dispatch Semaphore)

IOS low-level exploration of multithreading (11) – GCD source analysis (Dispatch_group)

1. Dispatch Source is introduced

The Dispatch Source is a wrapper around the BSD kernel’s customary kqueue, a technique that performs processing on the application programmer side when an event occurs in the XNU kernel.

It has a very low CPU load and uses as little resources as possible. When an event occurs, the Dispatch Source performs the processing of the event in the specified Dispatch Queue.

  • dispatch_source_create: create the source
  • dispatch_source_set_event_handler: Sets the source callback
  • dispatch_source_merge_data: Source event setting data
  • dispatch_source_get_data: Gets the source event data
  • dispatch_resume: Resume resume
  • dispatch_suspend: hang

In our daily development, we often use timer NSTimer, such as the countdown to send SMS, or the update of the progress bar. However, NSTimer needs to be added to NSRunloop and is affected by mode. It is interrupted when it is affected by other event sources. When sliding scrollView, mode will be switched and the timer will stop, resulting in inaccurate timer timing.

GCD provides a solution dispatch_source to come up with a similar requirement scenario.

  • The time is more accurate.CPUSmall load, less occupation of resources
  • You can use child threads to solve the UI problem of timer running on the main thread
  • You can pause, you can continue, you don’t have toNSTimerThe same needs to be created again

2. Dispatch the Source to use

The code to create the event source:

// Method declaration
dispatch_source_t dispatch_source_create(
        dispatch_source_type_t type,
        uintptr_t handle,
        unsigned long mask,
        dispatch_queue_t _Nullable queue);

// The implementation process
dispatch_source_t source =  dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0.0,  dispatch_get_main_queue());

Copy the code

When creating, you pass in two important parameters:

  • dispatch_source_type_t The type of source to create
  • dispatch_queue_t A scheduling queue for event processing

2.1 Type of Dispatch Source

  • Dispatch Source type:
  1. The DISPATCH_SOURCE_TYPE_DATA_ADD variable is increased

  2. DISPATCH_SOURCE_TYPE_DATA_OR variables OR

  3. DISPATCH_SOURCE_TYPE_DATA_REPLACE The newly obtained data value replaces the existing one

  4. DISPATCH_SOURCE_TYPE_MACH_SEND MACH port send

  5. DISPATCH_SOURCE_TYPE_MACH_RECV MACH port received

  6. DISPATCH_SOURCE_TYPE_MEMORYPRESSURE Memory pressure (note: available after iOS8)

  7. DISPATCH_SOURCE_TYPE_PROC Detected process-related events

  8. DISPATCH_SOURCE_TYPE_READ Reads file images

  9. DISPATCH_SOURCE_TYPE_SIGNAL Receives signals

  10. DISPATCH_SOURCE_TYPE_TIMER timer

  11. The DISPATCH_SOURCE_TYPE_VNODE file system is changed

  12. DISPATCH_SOURCE_TYPE_WRITE Writes file images

Example for designing a timer:

  • Hit the screen to start

usedispatch_sourceThe timer can be paused and started without being affected by the main threadUI eventsSo its timing is accurate. As shown below:

2.2 Precautions for use

Matters needing attention

  1. Source needs to be started manually

Dispatch Source is most used to realize timer. After Source is created, it is suspended by default and dispatch_resume needs to be manually called to start the timer. Dispatch_after simply encapsulates the call to the Dispatch source timer and then executes the defined block in the callback function.

  1. A circular reference

Because the dispatch_source_set_event_handle callback is a block, it is copied when added to the source list and is strongly referenced by the source. If the block holds self and self holds the source, it causes a circular reference. Therefore, the correct method is to cancel the timer by using weak+strong or calling dispatch_source_cancel in advance.

  1. Suspend calls to maintain a balance

There is a balance between dispatch_resume and dispatch_suspend calls. If dispatch_resume is called repeatedly, it crashes because it disestablishes the if branch of the dispatch_resume code. DISPATCH_CLIENT_CRASH(” Over-resume of an object “) was executed, causing the crash.

  1. Source creation and release timing

Source in the suspend state, setting source = nil or recreating source will cause crash. The correct way is to restore the resume with dispatch_source_cancel(source).

3. Dispatch Source Source analysis

Then go to the underlying source code to see why there are some problems above.

3.1 dispatch_resume

  • dispatch_resume
void
dispatch_resume(dispatch_object_t dou)
{
	DISPATCH_OBJECT_TFB(_dispatch_objc_resume, dou);
	if (unlikely(_dispatch_object_is_global(dou) ||
			_dispatch_object_is_root_or_base_queue(dou))) {
		return;
	}
	if(dx_cluster(dou._do) == _DISPATCH_QUEUE_CLUSTER) { _dispatch_lane_resume(dou._dl, DISPATCH_RESUME); }}Copy the code

Dispatch_resume will execute _dispatch_lane_resume

  • _dispatch_lane_resume

The method here is to judge the related state of the event source, if excessiveresumeRecovery, it willgotoGo to theover_resumeFlow, directly upDISPATCH_CLIENT_CRASHTo collapse.

There is also a determination of the pending count, which contains all pending and inactive bits. An underflow underflow means that an overrecovery or pause count needs to be moved to an edge count, that is, a continuous recovery is required if the current counter is not yet in a runnable state.

3.2 dispatch_suspend

  • Hang dispatch_suspend

dispatch_suspendCan also be found in the definition of, restore and suspend must beKeep the balanceA suspended object does not call any of its associated objectsblock. In any run associated with the objectblockWhen done, the object will be suspended.

void
dispatch_suspend(dispatch_object_t dou)
{
	DISPATCH_OBJECT_TFB(_dispatch_objc_suspend, dou);
	if (unlikely(_dispatch_object_is_global(dou) ||
			_dispatch_object_is_root_or_base_queue(dou))) {
		return;
	}
	if (dx_cluster(dou._do) == _DISPATCH_QUEUE_CLUSTER) {
		return_dispatch_lane_suspend(dou._dl); }}Copy the code
  • _dispatch_lane_suspend

  • _dispatch_lane_suspend_slow

Also here a pause is maintained to suspend the counter if it is called consecutivelydispatch_suspendThe unsigned underflow of the subtraction may occur because another thread may have touched the value when we tried to acquire the lock, or because another thread raced to perform the same operation and acquire the lock first.

So you can’t repeat the suspension or recovery, must be one of you one of me, you two of me two, keep a balanced.

4. To summarize

  • Use timerNSTimerNeed to joinNSRunloop, resulting in inaccurate count, can be usedDispatch SourceTo solve the
  • Dispatch SourcePay attention to the use ofrestoreandhangtobalance
  • sourceinsuspendIn state, if the value is directly setsource = nil Or recreate itsourceCan cause crash. The right way is inresumeState call downdispatch_source_cancel(source)Then create it again.
  • becausedispatch_source_set_event_handleThe callback isblock, before adding tosourceIs executed on the linked listcopyAnd wassourceStrong reference, ifblockHold in theself.selfHold againsourceThis will cause circular references. So the right way is to useweak+strongOr pretunedispatch_source_cancelcanceltimer.

More content continues to be updated

🌹 if you like, give it a thumbs up 👍🌹

🌹 feel harvest, can come to a wave, collection + attention, comment + forward, so as not to find me next time 😁🌹

🌹 welcome everyone to leave a message exchange, criticism and correction, learn from each other 😁, improve themselves 🌹