iOS信号量的使用

 

Does iOS have any kind of very low level condition lock that does not include locking?html

I am looking for a way to signal an awaiting thread from within the Core Audio render thread, without the usage of locks. I was wondering if something low level as a Mach system call might exist.ios

Right now I have a Core Audio thread that uses a non-blocking thread safe message queue to send messages to another thread. The other thread then pulls every 100ms to see if messages are available in the queue.git

But this is very rudimentary and the timing is awful. I could use condition locks, but that involves locking, and I would like to keep any kind of locking out of the rendering thread.程序员

What I am looking for is having the message queue thread wait until the Core Audio render thread signals it. Just like pthread conditions, but without locking and without immediate context switching? I would like the Core Audio thread to complete before the message queue thread is woken up.github

share improve this question
 
    
You want to do inter-thread signaling without any locking and/or context-switching? If you are queueing audio buffer pointers, why would locking and/or context-switching be any kind of bottleneck? – Martin James Dec 30 '13 at 17:35 
    
This is not about the audio rendering. It's about the Core Audio thread sending messages of what's going on: It switched playback from one audio buffer to another (a track change) or it has run out of audio frames to play (buffer under run), etc. So what happens is, it puts this message in a struct, which it writes to a non-blocking circular buffer. The buffer is then checked every now and then (100ms) by another thread, which in return act on the messages it receives from the rendering thread. So I am basically just looking for at way to push to another thread instead of pulling every 100ms. – Trenskow Dec 30 '13 at 17:41 

1 Answer

up vote 3down voteaccepted

Updated

dispatch_semaphore_t works well and is more efficient than a mach semaphore_t. The original code looks like this using a dispatch semaphore:web

#include <dispatch/dispatch.h>

// Declare mSemaphore somewhere it is available to multiple threads
dispatch_semaphore_t mSemaphore;


// Create the semaphore
mSemaphore = dispatch_semaphore_create(0);
// Handle error if(nullptr == mSemaphore)


// ===== RENDER THREAD
// An event happens in the render thread- set a flag and signal whoever is waiting
/*long result =*/ dispatch_semaphore_signal(mSemaphore);


// ===== OTHER THREAD
// Check the flags and act on the state change
// Wait for a signal for 2 seconds
/*long result =*/ dispatch_semaphore_wait(mSemaphore, dispatch_time(dispatch_time_now(), 2 * NSEC_PER_SEC));


// Clean up when finished
dispatch_release(mSemaphore);

Original answer:

You can use a mach semaphore_t for this purpose. I've written a C++ class that encapsulates the functionality: https://github.com/sbooth/SFBAudioEngine/blob/master/Semaphore.cpp编程

Whether or not you end up using my wrapper or rolling your own the code will look roughly like:安全

#include <mach/mach.h>
#include <mach/task.h>

// Declare mSemaphore somewhere it is available to multiple threads
semaphore_t mSemaphore;


// Create the semaphore
kern_return_t result = semaphore_create(mach_task_self(), &mSemaphore, SYNC_POLICY_FIFO, 0);
// Handle error if(result != KERN_SUCCESS)


// ===== RENDER THREAD
// An event happens in the render thread- set a flag and signal whoever is waiting
kern_return_t result = semaphore_signal(mSemaphore);
// Handle error if(result != KERN_SUCCESS)


// ===== OTHER THREAD
// Check the flags and act on the state change
// Wait for a signal for 2 seconds
mach_timespec_t duration = {
  .tv_sec = 2,
  .tv_nsec = 0
};

kern_return_t result = semaphore_timedwait(mSemaphore, duration);

// Timed out
if(KERN_OPERATION_TIMED_OUT != result)
  ;

// Handle error if(result != KERN_SUCCESS)


// Clean up when finished
kern_return_t result = semaphore_destroy(mach_task_self(), mSemaphore);
// Handle error if(result != KERN_SUCCESS)
share improve this answer
 
    
Thank you! This was exactly what I was looking for. I would give you a thousand in reputation if I could. I knew there would be some low level stuff I could use. Thanks a lot! PS. Will take a look at your wrapper. – Trenskow Dec 31 '13 at 0:42
    
It works like a charm... – Trenskow Dec 31 '13 at 1:46
    
I'm glad to hear it – sbooth Dec 31 '13 at 1:55
    
Using a lock-free queue or circular fifo to communicate from real-time audio callbacks to the UI has been recommended by several audio developers. Polling at display frame rate (60Hz or 16.6 mS or a CADisplayLink of 1, not 100 mS) will allow updating the UI at full frame rate with the minimum number of threads. – hotpaw2Dec 31 '13 at 20:32 
    
@hotpaw2 This is definitely true and depending on the purpose even updating at the display frame rate can be excessive (for example when updating the playback time often 5 times per second is adequate). – sbooth Jan 1 '14 at 15:52

 

复制代码
    //    建立一个信号量,值为0        
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        //    在一个操做结束后发信号,这会使得信号量+1
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {

            dispatch_semaphore_signal(sema);

        });
    //    一开始执行到这里信号量为0,线程被阻塞,直到上述操做完成使信号量+1,线程解除阻塞
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
复制代码

 

复制代码
    //    建立一个组 
    dispatch_group_t group = dispatch_group_create();
    //    建立信号 信号量为10
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    //    取得默认的全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for(inti = 0; i < 100; i++)
    {
        //    因为信号量为10 队列里面最多会有10我的任务被执行,
       dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
        //    任务加到组内被监听
        dispatch_group_async(group, queue, ^{
            NSLog(@"%i",i);
            sleep(2);
            dispatch_semaphore_signal(semaphore);
        });
    }
    //    等待组内全部任务完成,不然阻塞
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);
    dispatch_release(semaphore);    
 
 
 

block: block是c的一个运行时特性,和函数指针相似,用户回调函数。主要用于并行线程。多线程

复制代码
//建立一个分发队列,第一个参数为队列名,第二个参数是保留的 dispatch_queue 属性,设为null
//可以使用函数 dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags);来得到全局的 dispatch_queue,参数 priority 表示优先级,
dispatch_queue_t queue = dispatch_queue_create("test_queue", NULL);
//dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
//将一个 block 加入一个 dispatch_queue,这个 block 会再其后获得调度时,并行运行。一个queue能够加入多个block,这些 blocks 是按照 FIFO(先入先出)规则调度的,先加入的先执行,后加入的必定后执行,但在某一个时刻,可能有多个 block 同时在执行。实际结果是第一个执行完执行第二个。
dispatch_sync(queue, ^(void){
        for (int i=0; i<100; ++i) {
            NSLog(@"i:%d", i);
        }
});
复制代码

信号:sem并发

复制代码
//建立信号,将其资源初始值设置为 0 (不能少于 0),表示任务尚未完成,没有资源可用主线程不要作事情。
    __block dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    __block dispatch_semaphore_t sem2 = dispatch_semaphore_create(0);
    
    dispatch_queue_t queue = dispatch_queue_create("test_queue", NULL);
    dispatch_sync(queue, ^(void){
        for (int i=0; i<100; ++i) {
            NSLog(@" block 1 i:%d", i);
        }
        //增长 semaphore 计数(可理解为资源数),代表任务完成,有资源可用主线程能够作事情了。
        dispatch_semaphore_signal(sem);
    });
    dispatch_sync(queue, ^(void){
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        for (int i=0; i<100; ++i) {
            NSLog(@" block 2 i:%d", i);
        }
        dispatch_semaphore_signal(sem2);
    });
    
    //等待信号,主线程继续运行,减小 semaphore 的计数,若是资源数少于 0,则代表资源还可不得,我得按照FIFO(先等先得)的规则等待资源就绪,一旦资源就绪而且获得调度了,我再执行。
    dispatch_semaphore_wait(sem2, DISPATCH_TIME_FOREVER);
    dispatch_release(queue);
    dispatch_release(sem);
复制代码

 

group

将block加入到group,group中全部block执行完以后,主线程才能够继续运行

复制代码
//建立信号,将其资源初始值设置为 0 (不能少于 0),表示任务尚未完成,没有资源可用主线程不要作事情。
    __block dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    
    dispatch_queue_t queue = dispatch_queue_create("test_queue", NULL);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^(void){
        for (int i=0; i<100; ++i) {
            NSLog(@" block 1 i:%d", i);
        }
        //增长 semaphore 计数(可理解为资源数),代表任务完成,有资源可用主线程能够作事情了。
        dispatch_semaphore_signal(sem);
    });
    
    dispatch_block_t block2 = ^(void){
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        for (int i=0; i<100; ++i) {
            NSLog(@" block 2 i:%d", i);
        }
    };
    dispatch_group_async(group, queue, block2);
    
    //主线程等待block执行完成
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    dispatch_release(group);
    dispatch_release(queue);
    dispatch_release(sem);
复制代码

 

子线程运行完

复制代码
dispatch_async(getDataQueue,^{
    //获取数据,得到一组后,刷新UI.
    dispatch_aysnc (mainQueue, ^{
    //UI的更新需在主线程中进行
};
}
)
复制代码

 

 


GCD下的几种实现同步的方式

 

GCD多线程下,实现线程同步的方式有以下几种:

1.串行队列 2.并行队列 3.分组 4.信号量

实例: 去网上获取一张图片并展现在视图上. 实现这个需求,能够拆分红两个任务,一个是去网上获取图片,一个是展现在视图上. 这两个任务是有关联的,因此须要同步处理.

下面看这几种方式如何实现.

 

1、

1.串行队列

1.1[GCD相关:]

(1)GCD下的dispatch_queue队列都是FIFO队列,都会按照提交到队列的顺序执行.

只是根据队列的性质,分为<1>串行队列:用户队列、主线程队列 <2>并行队列. 

(2)同步(dispatch_sync)、异步方式(dispatch_async). 配合串行队列和并行队列使用.

1.2同步队列直接提交两个任务就能够.

复制代码
    // 串形队列
    dispatch_queue_t serilQueue = dispatch_queue_create("com.quains.myQueue", 0);
    
    //开始时间
    NSDate *startTime = [NSDate date];
    
    
    __block UIImage *image = nil;
    
    //1.先去网上下载图片
    dispatch_async(serilQueue, ^{
        NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
        NSURL *url = [NSURL URLWithString:urlAsString];
        
        NSError *downloadError = nil;
        
        NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
        
        if (downloadError == nil && imageData != nil) {
            image = [[UIImage imageWithData:imageData] retain];
        }
        else if(downloadError != nil){
            NSLog(@"error happened = %@", downloadError);
        }
        else{
            NSLog(@"No data download");
        }
    });
    
    //2.在主线程展现到界面里
    dispatch_async(serilQueue, ^{
        
        NSLog(@"%@",[NSThread currentThread]);
        
        // 在主线程展现
        dispatch_async(dispatch_get_main_queue(), ^{
        if (image != nil) {
            
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
            
            [imageView setImage:image];
            
            [imageView setContentMode:UIViewContentModeScaleAspectFit];
            [self.view addSubview:imageView];
            [imageView release];
            
            NSDate *endTime = [NSDate date];
            NSLog(@"串行异步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
        }
        else{
            NSLog(@"image isn't downloaded, nothing to display");
        }
        });
        
    });
    
    //3.清理
    dispatch_release(serilQueue);
    [image release];
复制代码

注意:

(1) __block变量分配在栈,retain下,防止被回收.

(2)dispatch要手动create和release.

(3)提交到主线程队列的时候,慎用同步dispatch_sync方法,有可能形成死锁. 由于主线程队列是串行队列,要等队列里的任务一个一个执行.因此提交一个任务到队列,若是用同步方法就会阻塞住主线程,而主线程又要等主线程队列里的任务都执行完才能执行那个刚提交的,因此主线程队列里还有其余的任务的话,但他已经被阻塞住了,无法先完成队列里的其余任务,即,最后一个任务也没机会执行到,因而形成死锁.

(4)提交到串行队列能够用同步方式,也能够用异步方式.

 

2.并行队列

采用并行队列的时候,能够采用同步的方式把任务提交到队列里去,便可以实现同步的方式

复制代码
//新建一个队列
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //记时
    NSDate *startTime = [NSDate date];
    
    //加入队列
    dispatch_async(concurrentQueue, ^{
        __block UIImage *image = nil;
        
        //1.先去网上下载图片
        dispatch_sync(concurrentQueue, ^{
            NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
            NSURL *url = [NSURL URLWithString:urlAsString];
            
            NSError *downloadError = nil;
            
            NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
            
            if (downloadError == nil && imageData != nil) {
                image = [UIImage imageWithData:imageData];
            }
            else if(downloadError != nil){
                NSLog(@"error happened = %@", downloadError);
            }
            else{
                NSLog(@"No data download");
            }
        });
        
        //2.在主线程展现到界面里
        dispatch_sync(dispatch_get_main_queue(), ^{
            if (image != nil) {
                UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
                [imageView setImage:image];
                
                [imageView setContentMode:UIViewContentModeScaleAspectFit];
                [self.view addSubview:imageView];
                [imageView release];
                
                NSDate *endTime = [NSDate date];
                NSLog(@"并行同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
            }
            else{
                NSLog(@"image isn't downloaded, nothing to display");
            }
        });
    });
复制代码

两个同步的任务用一个异步的包起来,提交到并行队列里去,便可实现同步的方式.

 

3.使用分组方式

3.1 group自己是将几个有关联的任务组合起来,而后提供给开发者一个知道这个group结束的点.

虽然这个只有一个任务,可是能够利用group的结束点,去阻塞线程,从而来实现同步方式.

复制代码
dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    NSDate *startTime = [NSDate date];
    
    __block UIImage *image = nil;
    
    dispatch_group_async(group, queue, ^{
        
        //1.先去网上下载图片
            NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
            NSURL *url = [NSURL URLWithString:urlAsString];
            
            NSError *downloadError = nil;
            
            NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
            
            if (downloadError == nil && imageData != nil) {
                image = [[UIImage imageWithData:imageData] retain];
            }
            else if(downloadError != nil){
                NSLog(@"error happened = %@", downloadError);
            }
            else{
                NSLog(@"No data download");
            }
        
        });
    
    // 2.等下载好了再在刷新主线程
    dispatch_group_notify(group, queue, ^{
        
        //在主线程展现到界面里
        dispatch_async(dispatch_get_main_queue(), ^{
            if (image != nil) {
                UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
                [imageView setImage:image];
                [image release];
                
                [imageView setContentMode:UIViewContentModeScaleAspectFit];
                [self.view addSubview:imageView];
                [imageView release];
                
                NSDate *endTime = [NSDate date];
                NSLog(@"分组同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
            }
            else{
                NSLog(@"image isn't downloaded, nothing to display");
            }
        });
        
    });
    
    // 释放掉
    dispatch_release(group);
复制代码

dispatch_group 也要手动建立和释放.

dispatch_notify()提供了一个知道group何时结束的点. 固然也可使用dispatch_wait()去阻塞.

 

4.信号量

信号量 和 琐 的做用差很少,能够用来实现同步的方式. 

可是信号量一般用在 容许几个线程同时访问一个资源,经过信号量来控制访问的线程个数.

复制代码
// 信号量初始化为1
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    NSDate *startTime = [NSDate date];
    
    __block UIImage *image = nil;
    
    
    //1.先去网上下载图片
    dispatch_async(queue, ^{
        
        // wait操做-1
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        // 开始下载
        NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
        NSURL *url = [NSURL URLWithString:urlAsString];
        
        NSError *downloadError = nil;
        
        NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
        
        if (downloadError == nil && imageData != nil) {

            image = [[UIImage imageWithData:imageData] retain];
            //NSLog(@"heap %@", image);
            //NSLog(@"%d",[image retainCount]);
        }
        else if(downloadError != nil){
            NSLog(@"error happened = %@", downloadError);
        }
        else{
            NSLog(@"No data download");
        }

        // signal操做+1
        dispatch_semaphore_signal(semaphore);
    });
    
  
    // 2.等下载好了再在刷新主线程
    dispatch_async(dispatch_get_main_queue(), ^{
        
        // wait操做-1
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        if (image != nil) {
            
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
            
            [imageView setImage:image];
            NSLog(@"%d",[image retainCount]);
            [image release];
            
            [imageView setContentMode:UIViewContentModeScaleAspectFit];
            [self.view addSubview:imageView];
            [imageView release];
            
            NSDate *endTime = [NSDate date];
            NSLog(@"信号量同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
        }
        else{
            NSLog(@"image isn't downloaded, nothing to display");
        }
        
        // signal操做+1
        dispatch_semaphore_signal(semaphore);
    });
复制代码

dispatch_wait会阻塞线程而且检测信号量的值,直到信号量值大于0才会开始往下执行,同时对信号量执行-1操做.

dispatch_signal则是+1操做.


semaphore_create

http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/semaphore_wait.html


Function - Create a new semaphore.

SYNOPSIS

kern_return_t	semaphore_create
		(task_t                   task,
		 semaphore_t        *semaphore,
		 int                    policy,
		 int                     value);

PARAMETERS

task
[in task port] The task receiving the send right of the newly created semaphore.

 

semaphore
[out send right] The port naming the created semaphore.

 

policy
[in scalar] The blocked thread wakeup policy for the newly created semaphore. Valid policies are:

 

SYNC_POLICY_FIFO
a first-in-first-out policy for scheduling thread wakeup.

 

SYNC_POLICY_FIXED_PRIORITY
a fixed priority policy for scheduling thread wakeup.

 

value
[in scalar] The initial value of the semaphore count.

DESCRIPTION

The semaphore_create function creates a new semaphore, associates the created semaphore with the specified task, and returns a send right naming the new semaphore. In order to support a robust producer/consumer communication service, Interrupt Service Routines (ISR) must be able to signal semaphores. The semaphore synchronizer service is designed to allow user-level device drivers to perform signal operations, eliminating the need for event counters. Device drivers which utilize semaphores are responsible for creating (via semaphore_create) and exporting (via device_get_status) semaphores for user level access. Device driver semaphore creation is done at device initialization time. Device drivers may support multiple semaphores.

RETURN VALUES

 

KERN_INVALID_ARGUMENT
The task argument or the policy argument was invalid, or the initial value of the semaphore was invalid.

 

KERN_RESOURCE_SHORTAGE
The kernel could not allocate the semaphore.

 

KERN_SUCCESS
The semaphore was successfully created.

RELATED INFORMATION

Functions: semaphore_destroysemaphore_signalsemaphore_signal_allsemaphore_waitdevice_get_status.

 
 

semaphore_wait


Function - Wait on the specified semaphore.

SYNOPSIS

kern_return_t   semaphore_wait
                (semaphore_t                          semaphore);

PARAMETERS

 

semaphore
[in send right] The port naming the semaphore that the wait operation is being performed upon.

DESCRIPTION

The semaphore_wait function decrements the semaphore count. If the semaphore count is negative after decrementing, the calling thread blocks. Device driver interrupt service routines (ISR) should never executesemaphore_wait, since waiting on a semaphore at the ISR level may, and often will, lead to a deadlock.

RETURN VALUES

 

KERN_INVALID_ARGUMENT
The specified semaphore is invalid.

 

KERN_TERMINATED
The specified semaphore has been destroyed.

 

KERN_ABORTED
The caller was blocked due to a negative count on the semaphore, and was awoken for a reason not related to the semaphore subsystem (e.g.  thread_terminate).

 

KERN_SUCCESS
The semaphore wait operation was successful.

RELATED INFORMATION

Functions: semaphore_createsemaphore_destroysemaphore_signalsemaphore_signal_alldevice_get_status.

 


那你在代码中是否很好的使用了锁的机制呢?你又知道几种实现锁的方法呢?

今天一块儿来探讨一下Objective-C中几种不一样方式实现的锁,在这以前咱们先构建一个测试用的类,假想它是咱们的一个共享资源,method1与method2是互斥的,代码以下:

1.使用NSLock实现的锁

看到打印的结果了吗,你会看到线程1锁住以后,线程2会一直等待走到线程1将锁置为unlock后,才会执行method2方法。

NSLock是Cocoa提供给咱们最基本的锁对象,这也是咱们常常所使用的,除lock和unlock方法外,NSLock还提供了tryLock和lockBeforeDate:两个方法,前一个方法会尝试加锁,若是锁不可用(已经被锁住),刚并不会阻塞线程,并返回NO。lockBeforeDate:方法会在所指定Date以前尝试加锁,若是在指定时间以前都不能加锁,则返回NO。

2.使用synchronized关键字构建的锁

固然在Objective-C中你还能够用@synchronized指令快速的实现锁:

@synchronized指令使用的obj为该锁的惟一标识,只有当标识相同时,才为知足互斥,若是线程2中的@synchronized(obj)改成@synchronized(other),刚线程2就不会被阻塞,@synchronized指令实现锁的优势就是咱们不须要在代码中显式的建立锁对象,即可以实现锁的机制,但做为一种预防措施,@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。因此若是不想让隐式的异常处理例程带来额外的开销,你能够考虑使用锁对象。

3.使用C语言的pthread_mutex_t实现的锁

pthread_mutex_t定义在pthread.h,因此记得#include <pthread.h>
4.使用GCD来实现的”锁”
以上代码构建多线程咱们就已经用到了GCD的dispatch_async方法,其实在GCD中也已经提供了一种信号机制,使用它咱们也能够来构建一把”锁”(从本质意义上讲,信号量与锁是有区别,具体差别参加信号量与互斥锁之间的区别):

至于代码产生的效果固然和上一例是如出一辙的,关于信号机制,熟悉C编程的你确定也不会陌生的,关于GCD中更多关于dispatch_semaphore_t的信息,能够跳转到本博客的这一往篇文章:GCD介绍(三): Dispatch Sources

好了,以上就是我所列举了几种方式来实现锁,固然锁大多数状况下也是配合多线程一块儿使用的,关于多线程编程,我这儿就不赘述了。

 

 

 

在上一文中,咱们已经讨论过用Objective-C锁几种实现(跳转地址),也用代码实际的演示了如何经过构建一个互斥锁来实现多线程的资源共享及线程安全,今天咱们继续讨论锁的一些高级用法。

1.NSRecursiveLock递归锁

平时咱们在代码中使用锁的时候,最容易犯的一个错误就是形成死锁,而容易形成死锁的一种情形就是在递归或循环中,以下代码:

 

 

以上的代码中,就是一种典型的死锁状况,由于在线程1中的递归block中,锁会被屡次的lock,因此本身也被阻塞了,因为以上的代码很是的简短,因此很容易能识别死锁,但在较为复杂的代码中,就不那么容易发现了,那么如何在递归或循环中正确的使用锁呢?此处的theLock若是换用NSRecursiveLock对象,问题便获得解决了,NSRecursiveLock类定义的锁能够在同一线程屡次lock,而不会形成死锁。递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操做。只有全部的锁住和解锁操做都平衡的时候,锁才真正被释放给其余线程得到。

2.NSConditionLock条件锁

当咱们在使用多线程的时候,有时一把只会lock和unlock的锁未必就能彻底知足咱们的使用。由于普通的锁只能关心锁与不锁,而不在意用什么钥匙才能开锁,而咱们在处理资源共享的时候,多数状况是只有知足必定条件的状况下才能打开这把锁:

 

 

在线程1中的加锁使用了lock,因此是不须要条件的,因此顺利的就锁住了,但在unlock的使用了一个整型的条件,它能够开启其它线程中正在等待这把钥匙的临界地,而线程2则须要一把被标识为2的钥匙,因此当线程1循环到最后一次的时候,才最终打开了线程2中的阻塞。但即使如此,NSConditionLock也跟其它的锁同样,是须要lock与unlock对应的,只是lock,lockWhenCondition:与unlock,unlockWithCondition:是能够随意组合的,固然这是与你的需求相关的。

3.NSDistributedLock分布式锁

以上全部的锁都是在解决多线程之间的冲突,但若是赶上多个进程或多个程序之间须要构建互斥的情景该怎么办呢?这个时候咱们就须要使用到NSDistributedLock了,从它的类名就知道这是一个分布式的Lock,NSDistributedLock的实现是经过文件系统的,因此使用它才能够有效的实现不一样进程之间的互斥,但NSDistributedLock并不是继承于NSLock,它没有lock方法,它只实现了tryLock,unlock,breakLock,因此若是须要lock的话,你就必须本身实现一个tryLock的轮询,下面经过代码简单的演示一下吧:

程序A:

 

 

程序B:

 

 

先运行程序A,而后当即运行程序B,根据打印你能够清楚的发现,当程序A刚运行的时候,程序B一直处于等待中,当大概10秒事后,程序B便打印出了appB:OK的输出,以上便实现了两上不一样程序之间的互斥。/Users/mac/Desktop/earning__是一个文件或文件夹的地址,若是该文件或文件夹不存在,那么在tryLock返回YES时,会自动建立该文件/文件夹。在结束的时候该文件/文件夹会被清除,因此在选择的该路径的时候,应该选择一个不存在的路径,以防止误删了文件。

 
相关文章
相关标签/搜索