This is the 19th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021.

Dispatch Semaphore is what we call a GCD Semaphore, a signal that holds counts.

Dispatch Semaphore provides three functions:

  1. dispatch_semaphore_creat: Creates a Semaphore and initializes the total amount of signals
  2. dispatch_semaphore_signal: Sends a signal that increases the total number of signals by 1
  3. dispatch_semaphore_wait: You can decrease the total semaphore by 1. When the total number of signals reaches 0, it will wait (blocking thread), otherwise it will execute normally.

Dispatch Semaphore is mainly used in practical development for:

  • Keep thread synchronization and convert asynchronous execution tasks to synchronous execution tasks
  • Keep the thread safe and lock the thread

Keep threads in sync:

dispatch_semaphore_t semaphore = dispatch_semaphore_creat(0);

__block NSInteger number = 0;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
    number = 100;
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

NSLog(@"semaphore---end,number = %zd",number);
Copy the code

The dispatch_SEMapHORE_wait lock blocks the current thread. After dispatch_semaphore_signal is unlocked, the current thread continues to execute.

Keep the thread safe and lock the thread

In thread safety dispatch_semaphore_wait can be treated as locking and dispatch_semaphore_signal as unlocking

_semaphore = dispatch_semaphore_creat(1);
Copy the code

Notice that the initial semaphore here is 1.

- (void)asyncTask{ dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); count++; sleep(1); NSLog(@" execute task: %zd",count); dispatch_semaphore_signal(_semaphore); }Copy the code

Asynchronously invoke asyncTask concurrently

for (NSInteger i = 0; i < 100; I++) {dispatch_async(dispatch_get_global_queue(0,0),^{[self asyncTask]; }); }Copy the code

Then I found that the print was executed sequentially from task 1 to 100, without the occurrence of two tasks being executed when I was upset.

The reason is as follows: If asyncTask is executed concurrently in a child thread, the first one added to the concurrent queue decreases the semaphore by 1, and the semaphore is equal to 0, and the next task can be executed. For other tasks in the concurrent queue, since the semaphore is not equal to 0 at this time, you must call dispatch_semaphore_signal to add 1 to the semaphore after the execution of the current task is completed, and then you can continue to execute the next task, and so on, so as to achieve the purpose of thread locking.

NSThreat+ Runloop implements resident threads

A common scenario for nsthreads in real life development is to implement resident threads.

  • Because each time to open a child thread will consume CPU, in the case of frequent use of child thread, frequent open child thread will consume a lot of CPU, and the creation of threads are completed after execution is released, can not be used again, so how to create a thread can make it work again? That is, create a resident thread.

First, resident threads since resident threads are resident, we can use GCD to implement a singleton to hold nsThreads

Static NSThread *shareThread = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate,^{ shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil]; [shareThread setName:@"threadTest"]; [shareThread start]; }); return shareThread; }Copy the code

Does this prevent thread creation from being destroyed?

[self performSelector:@selector(test) onThread:[viewController shareThread] withObject:nil waitUntilDone:NO];

- (void)test{

    NSLog(@"test:%@",[NSThread currentThread]);
}
Copy the code

The test method is not called if it is not printed. You can use runloop to keep the thread resident

Static NSThread *shareThread = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate,^{ shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil]; [shareThread setName:@"threadTest"]; [shareThread start]; }); return shareThread; } - (void)threadTest{ @autoreleasepool{ NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] formode:NSdefaultRunLoopMode]; [runLoop run]; }}Copy the code

And then I call performSelector and I get printed.