Preface:

In the studyIOS Performance Monitoring (2) — Main Thread Lag Monitoring,

Found that there were someGCDKnowledge of semaphores has not been thoroughly combed out before.

Therefore, this article is used to sort outGCDIn the semaphoredispatch_semaphore_tRelevant knowledge.


Introduction to Semaphore

Semaphore is a safeguard in multithreaded environments that can be used to ensure that two or more critical code is not called concurrently.

Before entering a critical code section, the thread must acquire a semaphore. Once the execution is complete, the thread releases the semaphore. Wait for the next semaphore to be sent before the thread can continue to acquire the new semaphore and execute the key snippet again.

  • Requirements:threadOne must be obtained before entering a critical code snippetA semaphore. (signalsignalAnd such as signalwaitThere should be one to one correspondence)
  • Function: Ensures that critical code segments are not called concurrently.

Here’s an example:

There is only room for five cars in one parking lot. At that moment, six cars arrived. Only the first five go in. The sixth car waits until one of the cars leaves the lot before entering.

here

Want to enter the parking lot — create a signal,

There is a parking space at present, take the card to enter — signal,

Currently no parking space, waiting in line for a card — waiting for a signal,

Leave the parking lot — destroy the signal.

In general, semaphores have four operations.

  1. Initialization signal (initialize/create)
  2. Signal (signal/post)
  3. Signal (such aswait/suspend)
  4. Signal release (destroy)

GCD semaphore (dispatch_semphore_t)

In iOS development, the first thing that comes to mind is dispatch_semphore_t in GCD.

1. Create a semaphore

  • Methods:dispatch_semaphore_create(long value)
dispatch_semaphore_create(long value); / /! < Create semaphoreCopy the code
  • Description:
parameter instructions
value The initial number of semaphore (>=0).

Note: Passing a value less than zero will return NULL.

If value > 0, a semaphore is created and the value signals are emitted simultaneously. If value = 0, it simply creates a semaphore without sending a signal. If value < 0, failure returns NULL.

2. Send semaphore

  • Methods:dispatch_semaphore_signal(dispatch_semaphore_t dsema);
dispatch_semaphore_signal(dispatch_semaphore_t dsema); / /! < Send semaphoreCopy the code
  • Description:
parameter instructions
dispatch_semaphore_t The semaphore passed in for the signal to be sent.

Dispatch_semaphore_t signal count +1.

3. Wait for the semaphore

  • Methods:dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); / /! < Wait for semaphoreCopy the code
  • Description:
parameter instructions
dispatch_semaphore_t The semaphore passed in for the signal to be waited for.

dispatch_semaphore_tSignal count of -1.
dispatch_time_t Timeout waiting time. After that time, returns non-zero and executes directly down.

It can also be set toDISPATCH_TIME_FOREVERWait forever.
The return value instructions
Int Returns 0 on success, non-0 on timeout.

The application of semaphore

Use a semaphore to cause an “asynchronous” thread to complete a “synchronous” operation.

Even in multi-threaded concurrent scenarios, the synchronization of operations can be ensured by controlling semaphores.

For example: In general, we want to implement asynchronous threads to perform synchronous operations. There are two ways to do this:

1. The first: use serial queue + asynchronous operation.

In this case, only one sliver thread is opened and sequential operations are performed.

    dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"111:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"222:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"333:%@",[NSThread currentThread]);
    });
Copy the code

This approach has some drawbacks:

First: since it is an asynchronous operation, a new child thread will be opened. It is also a serial queue, so only one sliver thread will be opened for synchronous operation. Lose the advantage of multithreading.

Second: you need to write it in a method, whereas in real development, you might asynchronously distribute it across methods, but you want to execute it serially.

2. The second kind: the use of semaphore, control of multi-threaded synchronization operation.

dispatch_semaphore_t sem = dispatch_semaphore_create(0); Dispatch_async (dispatch_get_global_queue(0, 0), ^{NSLog(@" task 1:%@",[NSThread currentThread]); dispatch_semaphore_signal(sem); }); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); Dispatch_async (dispatch_get_global_queue(0, 0), ^{NSLog(@" task 2:%@",[NSThread currentThread]); dispatch_semaphore_signal(sem); }); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); Dispatch_async (dispatch_get_global_queue(0, 0), ^{NSLog(@" task 3:%@",[NSThread currentThread]); });Copy the code

Of course, this is just an example. In practice, a signal and a wait are usually paired. Also, it is usually called separately in different methods.

For example, in iOS Performance Monitoring (2) – Main thread Lag monitoring, a signal will be sent when the main thread CommonModes change. At the same time, a sliver loop will be opened to continuously listen for CommonModes changes and wait for signals. In some cases, a timeout wait indicates that the main thread is currently stalled. Saving the current main thread method call stack serves monitoring purposes.

PS: detailed implementation, can be viewed in the QiLagMonitor source code.