What is dispatch sources?
In short, dispatch source is an object that monitors certain types of events. When these events occur, it automatically puts a block into the execution routine of a dispatch queue.
It seems a bit ambiguous. What types of events are discussed?
The following are events supported by GCD 10.6.0:
- Mach port send right state changes.
- Mach port receive right state changes.
- External process state change.
- File descriptor ready for read.
- File descriptor ready for write.
- Filesystem node event.
- POSIX signal.
- Custom timer.
- Custom Event.
This is a bunch of useful things. It supports all events supported by kqueue (what is kqueue? See http://en.wikipedia.org/wiki/Kqueue) and Mach (what is Mach? See http://en.wikipedia.org/wiki/Mach_ (kernel) ports, built-in timer support (so we don't have to use timeout parameters to create our own timer) and user events.
User events
Most of these events can be understood by name, but you may want to know what a user event is. Simply put, such events are signals sent to you by calling the dispatch_source_merge_data function.
This name is too strange for a function that sends an event signal. The reason for this name is that gcd automatically joins multiple events before the event handle is executed. You can "splice" the data to the dispatch source for any time, and if the dispatch queue is busy during this period, gcd will only call the handle once (do not think this is a problem, after reading the following content, you will understand ).
There are two types of user events:Dispatch_source_type_data_add
AndDispatch_source_type_data_or
The user event source hasUnsigned long data
Attribute.Unsigned long
InputDispatch_source_merge_data
. When using_ Add
When the event is joined, these numbers are added. When using_ Or
When the event is joined, these numeric logic and operations are performed. When the event handle is executed, we can use the dispatch_source_get_data function to access the current value, and then the value is reset to 0.
Let me assume a situation. Assume that some asynchronous executionCodeA progress bar is updated. Because the main thread is just another dispatch queue of GCD, we can push the GUI update work to the main thread. However, there may be a lot of such events, and we don't want to perform frequent and cumbersome updates to the GUI. Ideally, all the changes will be linked when the main thread is busy.
The use of dispatch source is perfect. With dispatch_source_type_data_add, We can splice the work, and then the main thread can know how many changes have taken place since the last event was handled, then update the entire segment to the progress bar.
Let's not talk about anything. Go to the Code:
Dispatch_source_t source = dispatch_source_create (values, 0, 0, values (); values (source, ^ {[progressindicator incrementby: values (source)] ;}); dispatch_resume (source ); dispatch_apply ([array count], globalqueue, ^ (size_t index) {// do some work on data at index dispatch_source_merge_data (source, 1 );});
(For this code, I would like to talk about something. When I used dispatch source for the first time, I had to struggle for a long time and it really crashed:The default status of dispatch source is suspended when it is started. After creation, it must be automatically restored. Otherwise, the event will not be transmitted or executed.)
Suppose you have set the min/max value of the progress bar, then this code is perfect. Data is processed concurrently. After each piece of data is complete, we will notify the dispatch source and add the dispatch source data to 1. In this way, we think that the work of a unit is complete. The event handle updates the progress bar based on the completed unit of work. If the main thread is idle and these units are slow, the event handle will be called and updated in real time when each unit is completed. If the main thread is busy with other work or the unit of work is quickly completed, the completion event will be linked, and the progress bar will be updated only when the main thread becomes available, and update the accumulated changes to the GUI at one time.
Now you may think, it sounds good, but what if I don't want to link events? Sometimes you may want to make every signal respond, and do not need any background intelligence. Ah .. In fact, it's easy to put your thoughts out of the box of imprisonment. If you want to get a response for every signal, you can use the dispatch_async function. In fact, the only reason for using dispatch source instead of dispatch_async is to take advantage of the connection.
Built-in events
The above is how to use user events, so what about built-in events? Take a look at the following example and use GCD to read standard input:
Export globalqueue = values (values, 0); dispatch_source_t stdinsource = dispatch_source_create (values, stdin_fileno, 0, globalqueue); values (stdinsource, ^ {char Buf [1024]; int Len = read (stdin_fileno, Buf, sizeof (BUF); If (LEN> 0) nslog (@ "got data from stdin: %. * s ", Len, Buf) ;}); dispatch_resume (stdinsource );
It's easy! Because we use a global queue, the handle is automatically executed in the background, andProgramOther parts of parallel, which means to speed up the case: when the event enters the program, the program is processing other transactions.
This is the benefit of a standard UNIX method to handle transactions, and you do not need to write a loop. If you use the classicRead
Call, we have to be very careful, because the returned data may be less than the request, and we have to endure the "errors" with no headers, suchEintr
(Terminal System Call ). With GCD, we don't need to worry about anything, so we can get rid of these pain points. If we leave unread data in the file descriptor, gcd will call our handle again.
This is fine for standard input, but for other file descriptors, we must consider how to clear the descriptors after reading and writing. When the dispatch source is still active, we must not disable the descriptor. If another file descriptor is created (possibly created by another thread) and the new descriptor is allocated with the same number, then your dispatch source may suddenly enter the read/write status when it is not appropriate. De is not a fun bug.
The proper clearing method is to useDispatch_source_set_cancel_handler
And input a block to close the file descriptor. Then we useDispatch_source_cancel
To cancel the dispatch source so that the handle is called and the file descriptor is disabled.
The use of other dispatch source types is similar. In general, you provide a source (Mach port, file descriptor, process ID, and so on) delimiter as the diapatch source handle. The mask parameter is usually not used,Dispatch_source_type_proc
For example, Mask refers to the process event we want to accept. Then we provide a handle, and then restore the source (as I mentioned in the bold font, we must restore it first. Dispatch source also provides a source-specific data. We useDispatch_source_get_data
Function to access it. For example, the file descriptor provides the approximate number of available bytes. The process source will give the mask of the event that occurred after the last call. For more information about the meaning of data given by each source, see man page.
Timer
Timer events are slightly different. They do not use the handle/mask parameter, and the timer event uses another function.Dispatch_source_set_timer
To configure the timer. This function uses three parameters to control timer triggering:
Start
The parameter controls the time when the timer is triggered for the first time. The parameter type isDispatch_time_t
This is an opaque type and cannot be operated directly. We needDispatch_time
AndDispatch_walltime
Function to create them. In addition, constantsDispatch_time_now
AndDispatch_time_forever
It is usually useful.
Interval
There is nothing to explain about the parameters.
Leeway
Parameters are interesting. This parameter tells the system the accuracy of timer triggering. All timers do not guarantee 100% accuracy. this parameter is used to tell the system how much effort you want the system to ensure accuracy. If you want a timer to be triggered once in less than five seconds, and the more accurate the better, you can pass 0 as the parameter. In addition, if it is a periodic task, such as checking email, you will want to check every ten minutes, but not so precise. Therefore, you can pass in 60 to tell the system that the 60-second error is acceptable.
What does this mean? In short, it is to reduce resource consumption. If the system can take a long enough time to rest the CPU and execute a task set every time it wakes up, rather than waking up and sleeping to execute the task, the system will be more efficient. If you input a relatively large leeway to your timer, it means you allow the system to delay your timer to combine the timer task with other tasks for execution.
Summary
Now you know how to use the dispatch source function of GCD to monitor file descriptors, timers, linked user events, and other similar behaviors. Because dispatch source is fully integrated with dispatch queue, you can use any dispatch queue. You can execute a dispatch source handle in the main thread, concurrently execute in the global queue, or execute it in a serial mode in the user queue (during execution, the operations of other modules of the program are taken into account ).
In the next article, I will discuss how to suspend, recover, and reset the dispatch queue, how to use dispatch semaphore, and how to use the one-time initialization function of GCD.
Original translated from: http://www.dreamingwish.com/dream-2012/gcd%E4%BB%8B%E7%BB%8D%EF%BC%88%E4%B8%89%EF%BC%89-dispatch-sources.html