首页 > 代码库 > Dispatch Semaphore
Dispatch Semaphore
A dispatch semaphore(信号量) is useful if you need a concurrency control for a small portion(部分) of the source code that has smaller granularity(颗粒度) than a serial dispatch queue or dispatch_barrier_async function.
If data are updated concurrently, inconsistent(不一致的) data might occur or the application might crash. You can avoid that by using a serial dispatch queue or the dispatch_barrier_async function. But sometimes concurrency control has to be done in smaller granularity.
(其实iOS的dispatch semaphore和Windows系统的Semaphore的作用是一样的,都是用来作线程同步或者说资源的互斥访问的。当然在Windows系统下,除了Semaphore之外,CriticalSection、Event、Mutex也可以完成此功能。)
Let’s see an example to show how to add all the data to an NSMutableArray when the order is unimportant.
1 2 3 4 5 6 7 8 9 10 | dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); NSMutableArray*array=[[NSMutableArrayalloc] init]; for(inti=0;i<100000;++i){ dispatch_async(queue,^{ [array addObject:[NSNumber numberWithInt:i]]; }); } |
But, because the NSMutableArray class doesn’t support multithreading, when the object is updated from many threads, it will be corrupted(损坏). The application probably crashes because of a memory-related problem. This is a race condition(竞态[争]条件). We can use a dispatch semaphore in this case.
A dispatch semaphore is a semaphore with a counter. When the counter is zero, the execution waits. When the counter is more than zero, it keeps going after it decrements the counter.
The dispatch_semaphore_create function creats a dispatch semaphore.(相当于Windows系统的CreateSemaphore)
1 | dispatch_semaphore_t semaphore=dispatch_semaphore_create(1); |
The argument is an initial value of the counter. As its name includes “create”, you have to release it with the dispatch_release function. You can have ownership by calling the dispatch_retain function as well.
1 | dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER); |
A dispatch_semaphore_wait function waits until the counter of the dispatch semaphore becomes one and more. When the counter is one and more, or the counter becomes one and more while it is waiting, it decreases the counter and returns from the dispatch_semaphore_wait function. The second argument specifies how long it waits in dispatch_time_t type. In this example, it waits forever. The return value is the same as that of the dispatch_group_wait function.
1 2 3 4 5 6 7 8 9 10 11 12 13 | dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW,1ull*NSEC_PER_SEC); longresult=dispatch_semaphore_wait(semaphore,time); if(result==0){ //The counter of the dispatch semaphore was more than one. //Or it became one and more before the specified timeout. //The counter is automatically decreased by one. //Here, you can execute a task that needs a concurrency control. }else{ //Because the counter of the dispatch semaphore was zero, //it has waited until the specified timeout. } |
When a dispatch_semaphore_wait function returns zero, a task that needs a concurrency control can be executed safely. After finish the task, you have to call thedispatch_semaphore_signal(相当于Windows系统的ReleaseSemaphore) function to increase the counter of the dispatch semaphore by one.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); //Set the initial value 1 for the counter of the dispatch semaphore //to assure that only one thread will access the object of //NSMutableArray class at the same time. dispatch_semaphore_t semaphore=dispatch_semaphore_create(1); NSMutableArray*array=[[NSMutableArrayalloc] init]; for(inti=0;i<100000;++i){ dispatch_async(queue,^{ dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER); //The counter of the dispatch semaphore is always zero here. //Because only one thread can access the object of the NSMutableArray class //at the same time, you can update the object safely. [array addObject:[NSNumber numberWithInt:i]]; //you have to call the dispatch_semaphore_signal function //to increase the counter of the dispatch semaphore. //If some threads are waiting for the dispatch_semasphore //the first thread will be started. dispatch_semaphore_signal(semaphore); }); } //Originally, because the dispatch semaphore isn’t needed any more, //you have to release the dispatch semaphore. //dispatch_release(semaphore); |
整理自《Multithreading and Memory Management for iOS and OS X》