Recently, multithreading was used in the project, but I forgot something about semaphores. After reviewing relevant materials, I will talk about this in detail based on my own understanding. Semaphores are flexible and playable. So the first thing we need to do is we need to understand three functions, and they are dispatch_semaphore_create(intptr_t value), dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout), dispatch_semaphore_signal(dispatch_semaphore_t dsema)

Take a look at the official documentation for these three functions, which I have translated briefly in broken English

The first dispatch_semaphore_create

/ *! * @function dispatch_semaphore_create * * @abstract * Creates new counting semaphore with an initial value. * * @ Discussion * Passing zero for the value is useful for when two threads need to reconcile * the completion of a particular event. Passing a value greater than zero is * useful for managing a finite pool of resources,  where the pool size is equal * to the value. Passing a value greater than 0 is only useful when two threads are mediating. Passing a value greater than 0 is only useful when mediating a thread pool. The size of The thread pool is equal to this value * * @param value * The starting value for The semaphore  a value less than zero will * cause NULL to be returned. * * @result * The newly created semaphore, or NULL on failure. */Copy the code

This function creates a semaphore, Parameter value into an integer variables, the value of this variable if less than 0 will return empty, so it is important to note that when it’s created to greater than or equal to 0 variable, the variable representing the two meanings, if pass, such as 2, said the initial reference count of the semaphore to 2, specific application later and speak will say

Now we create a semaphore and print it by placing the breakpoint on the next line

Where value represents the reference count of the current semaphore and orig represents the value passed in at initialization

Second dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); Look at the comments

/ *! * @function dispatch_semaphore_wait * * @abstract * Wait (decrement) for a semaphore. * * @discussion * Decrement the counting semaphore. If the resulting value is less than zero, * this function waits for a signal to occur before returning. If The value of The current semaphore is less than zero after subtracting, The function will wait for The value of signal before returning. Signal is dispatch_semaphore_signal. The result of passing NULL in this parameter is undefined. * * @param timeout * When to timeout (see dispatch_time). As a convenience there are the * DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER constants. DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER * * @result * Returns zero on success or non-zero if the timeout occurred. */Copy the code

Wait the word is contained in this function, the GCD is contained in the word is generally have the function of blocking the thread, this function also has the function, comments in writing cannot be empty, so we were introduced to this parameter is important to note that this function will do to the current semaphore subtract 1, if done after subtracting the semaphore count is less than 0 The second parameter is dispatch_time. We have two variables DISPATCH_TIME_NOW and DISPATCH_TIME_NOW “DISPATCH_TIME_FOREVER” means “not waiting” or “waiting forever”, which means blocking the current thread for a certain amount of time, so this function needs to be paired with a third function. Let’s test this by writing a code in the main thread using two defined parameters

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"~~~ I don't know if I got printed ~~~~");Copy the code

Here we pass in a semaphore of zero, and then when we decrement it by one it’s less than zero, the console won’t print, because it’s jammed in the main thread, the UI is stuck, clicking on anything doesn’t respond, if we pass in DISPATCH_TIME_NOW, that wouldn’t happen

If we pass 1 at creation time and run this code, we’ll see a printout, but it’ll crash, because two threads waiting for each other will cause a deadlock, which requires a third function

The third function dispatch_semaphore_signal(dispatch_semaphore_t dsema)

/ *! * @function dispatch_semaphore_signal * * @abstract * Signal (increment) a semaphore. * * @ Discussion * Increment the counting semaphore. If the previous value was less than zero, * this function wakes a waiting thread before returning. Counting semaphore (counting semaphore, counting semaphore, counting semaphore, counting semaphore, counting semaphore, counting semaphore, counting semaphore, counting semaphore) parameter is undefined. * * @result * This function returns non-zero if a thread is woken. Otherwise, zero is * returned. Returns a non-zero value if the thread is awake, or zero */ if notCopy the code

This function increments the current reference count by one and wakes up the waiting thread if the semaphore reference count is less than zero before calling the function. So this function pairs a function and does something to the thread, the same code as above

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"~~~ I didn't know I was printed ~~~~"); dispatch_semaphore_signal(semaphore);Copy the code

The semaphore is subtracted and then added, and the thread is free. Here is my understanding of semaphores.

The whole thread pool is like a parking lot, but the parking lot is full of cars, and when we create the semaphore, we pass in a value of 2 When we call dispatch_semaphore_wait, when you park your car, there is no parking space, and then you want to block the entrance so that other cars will not drive inside. At this time, you find two people in the car and don’t know what to do. Maybe you have to go, just think about it, maybe someone is going to come out, and the wait time is dispatch_time_t, you can wait forever, you can go right away, you can wait an hour, and when we call dispatch_semaphore_signal,signal is The signal means that the man sitting in the car his wife has called him, the ghost still does not come back, every day out of the house, the man quickly drove away from the parking lot, this time there is a parking space, and you pidianpidianpidian hurry to park the car, and then the entrance is unblocked.

Semaphores are very flexible, so let’s talk about their practical use in a project.

1, we know that a concurrent queue of threads in the order is not controllable, but sometimes we need a few request in accordance with the order, some people say why not from synchronous serial queue execution, but that is not fully play the role of multi-core, can open thread, not bad money, but also cannot create too many threads, a few or no problem, a lot of money No, there’s still a little money. Here I use YTK framework request, jingdong free interface test, by the way,YTK itself integrates this function, interested students can go to Githup to see the official documents, the code

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block long resultNumber; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [LQNewsApi.new startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) { NSLog(@" request successful ~~~~1~~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);} Failure :^(__kindof YTKBaseRequest * _Nonnull request) {NSLog(@" request failed ~~~~~1~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);}]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [LQNewsApi.new startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) { NSLog(@" request successful ~~~~2~~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);} Failure :^(__kindof YTKBaseRequest * _Nonnull request) {NSLog(@" request failed ~~~~~2~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);}]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [LQNewsApi.new startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) { NSLog (@ "request success ~ ~ ~ ~ ~ ~ ~ ~ ~");} failure: ^ (__kindof YTKBaseRequest * _Nonnull request) {NSLog (@ "three attempt failed ~ ~ ~ ~ ~ ~ ~ ~ ~");}]; });Copy the code

The results of

If all three requests are executed at the same time, the order of execution is out of control. If we wait for the last request to complete before executing the next one, we can control the order

2, when we have more than one request together, such as upload more pictures, more threads open can lead to excessive memory consumption, this time we need to control the number of threads, such as maximum upload two at the same time, two zhang zhang upload to upload again after a two, this time we can use a semaphore created when the incoming value to control the concurrency, or just three, please Find, we pass in 2 when we create the semaphore

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); __block long resultNumber; dispatch_async(quene, ^{ dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@" request start ~~~~1~~~~~"); [LQNewsApi.new startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) { NSLog(@" request successful ~~~~1~~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);} Failure :^(__kindof YTKBaseRequest * {resultNumber = dispatch_semaphore_signal(semaphore); NSLog(@" request failed ~~~~~1~~~~");}]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@" request start ~~~~2~~~~~"); [LQNewsApi.new startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) { NSLog(@" request successful ~~~~2~~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);} Failure :^(__kindof YTKBaseRequest * _Nonnull request) {NSLog(@" request failed ~~~~~2~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);}]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@" request start ~~~~3~~~~~"); [LQNewsApi.new startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) { NSLog(@" request successful ~~~~3~~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);} Failure :^(__kindof YTKBaseRequest * _Nonnull request) {NSLog(@" request failed ~~~~~3~~~~"); resultNumber = dispatch_semaphore_signal(semaphore);}]; resultNumber = dispatch_semaphore_signal(semaphore); }); }Copy the code

We introduced into 2 at the beginning, it shows that the semaphore count is 2, minus 1, respectively, in front of the former two requests when the third count is zero, this time will block the current thread, wait until after the two threads before a certain request, count + 1, and then began the third request, then hit the concurrency control purposes

The third purpose is 3, semaphore can be used as a thread lock, guarantee the security of data, we all know that when multiple threads at the same time operating a data will appear problem, including loss of data, the problem such as collapse, this time we need to lock to ensure the integrity of the data, then do a simple multithreaded operation examples of the same data at the same time, first take a look at no What happens to the lock.

#import "HomeSubViewController2.h" @interface HomeSubViewController2 () { dispatch_semaphore_t sem; } @property(nonatomic, assign) NSInteger leftNumber; @property(nonatomic, strong) NSMutableDictionary *muDic; @end @implementation HomeSubViewController2 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = UIColor.whiteColor; self.muDic = [NSMutableDictionary dictionary]; sem = dispatch_semaphore_create(1); } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self.muDic removeAllObjects]; self.leftNumber = 100; dispatch_queue_t queueOne = dispatch_queue_create("testOne", DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t queueTwo = dispatch_queue_create("testTwo", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queueOne, ^{ [self testCount]; }); dispatch_async(queueTwo, ^{ [self testCount]; }); } - (void)testCount{ while (1) { // NSInteger num = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); if (self.leftNumber > 0) { self.leftNumber--; NSLog(@"~~~~~~LeftNum:%d~~~%@",(int)self.leftNumber,[NSThread currentThread]); [self.muDic setValue:@(self.leftNumber) forKey:[NSString stringWithFormat:@"%02d",(int)self.leftNumber]]; [NSThread sleepForTimeInterval: 0.1]; }else{ NSLog(@"~~~~~~~~~~~~~over %d~~~~~",(int)self.muDic.count); // dispatch_semaphore_signal(sem); break; } // dispatch_semaphore_signal(sem); } } @endCopy the code

This is all the code for a controller that creates a global semaphore. Two threads simultaneously insert data into the dictionary. Theoretically, the dictionary should store 100 items of data, but because it is nonatomic, it is not safe to lose data as a result

Comments are then removed to ensure that no other thread will operate on each operation and data integrity is guaranteed

Conclusion: Semaphore’s role in a multi-threaded or many, in addition to the above mentioned, there are other purposes, such as can also be combined group use, principle of about the same, will not go into here, students are interested in private can have a try, pay attention to the point is to ensure that the reference count of a semaphore must in pairs, the subtraction operation must do addition operation, otherwise It is easy to block the thread, with more slowly skilled, personal expression ability is limited, there are mistakes welcome to correct.

PS: This article is original, reprint need to indicate