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時,會自動建立該文件/文件夾。在結束的時候該文件/文件夾會被清除,因此在選擇的該路徑的時候,應該選擇一個不存在的路徑,以防止誤刪了文件。

 
相關文章
相關標籤/搜索