423,GCD中(同步執(zhí)行(sync)和異步執(zhí)行(async)兩者的區(qū)別<是否等待隊列的任務(wù)執(zhí)行結(jié)束>,以及<是否具備開啟新線程>的能力 (多線程同步的方式:1,通過線程加鎖 2,串行隊列 3,...

所以這里同步應(yīng)該不是一起、而是共同完成的意思,可理解為協(xié)調(diào)就是按預(yù)定的先后次序進行工作,好比:不要和我搶了,你先等會我做完了你在做

線程同步目的為了多個線程都能很好的工作,合理的訪問系統(tǒng)資源不爭不搶、和諧共處。iOS開發(fā)中常用的保持線程同步有以下幾種:

  • 通過線程加鎖
  • 串行隊列
  • GCD

線程加鎖

常用的幾種形式的鎖

  • 1、 @synchronized
- (void)myMethod:(id)anObj
{
    @synchronized(anObj)
    {
        //執(zhí)行的代碼操作
    }
}

通過synchronized指令自動的添加一個互斥鎖,底層通過pthread_mutex實現(xiàn)。通過對一段代碼的使用進行加鎖。其他試圖執(zhí)行該段代碼的線程都會被阻塞,直到加鎖線程退出執(zhí)行該段被保護的代碼段。

當(dāng)在@synchronized()代碼塊中拋出異常的時候,Objective-C運行時會捕獲到該異常,并釋放信號量,并把該異常重新拋出給下一個異常處理者。

一個線程是可以以遞歸的方式多次調(diào)用myMethod。

關(guān)于參數(shù)anObj;

作為一個唯一標識符來標記當(dāng)前線程加鎖操作必須是個對象類型,所以對于同一個操作不同的線程應(yīng)該用同一個對象,否則無法起到標記加鎖的作用。 不能為空nil。

常見的基本都是self

@synchronized(self)
{
    //執(zhí)行的代碼操作
}

self作為標記符十分常見,但是很明顯會有一個問題:

//方法1
- (void)myMethod1:(id)anObj
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0), ^{
        @synchronized(anObj)
        {
            //執(zhí)行的代碼操作
        }
    });
}
//方法2
- (void)myMethod2:(id)anObj
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0), ^{
        @synchronized(anObj)
        {
            //執(zhí)行的代碼操作
        }
    });
}

myMethod1(self);
myMethod2(self)

如果myMethod1、myMethod2沒用任何關(guān)系,如果此時執(zhí)行myMethod1,那么myMethod2就只能等待其執(zhí)行完成。所以這種情況更細的粒度來加鎖,使用各自的對象互不影響更為合理。

  • 2、NSLock
NSLock * lock = [[NSLock alloc]init];
[lock lock];
//執(zhí)行的代碼操作
[lock unlock];  

底層通過pthread_mutex實現(xiàn);方法lock、unlock必須成對出現(xiàn),必須在同一個線程中操作否則無效。不支持遞歸,如果多次調(diào)用會造成死鎖。

如果多個線程共用一個lock ,一個線程加鎖后其他請求加鎖的線程會形成一個等待隊列、按照先進先出的規(guī)則等待鎖釋放后再加鎖(待驗證)。

  • 3、NSRecursiveLock 遞歸鎖類似NSLock,但它可以在同一個線程中反復(fù)加鎖且不會造成死鎖。

  • 4、 NSCondition 基于信號量方式實現(xiàn)的鎖對象,提供單獨的信號量管理接口。底層通過pthread_cond_t實現(xiàn)。

NSCondition對象包含鎖和條件檢測功能,類似于生產(chǎn)者和消費者:消費者消費資源如果沒有就繼續(xù)等待,生產(chǎn)者提供資源然后發(fā)出信號激活消費者。鎖的作用就是用來保護這一操作防止被其他線程干擾。不管你是大牛還是小白都歡迎入駐

    isWait = true;
    condition = [[NSCondition alloc]init];
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _user];
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _produce];
    });
-(void)_user{
    [condition lock];

    while (isWait) {
        //等待其他線程發(fā)出信號,[condition signal];
        //阻塞當(dāng)前線程
        NSLog(@"等待條件滿足");
        [condition wait];
    }
    {
        //執(zhí)行操作
        NSLog(@"執(zhí)行操作");
    }

    //完成
    [condition unlock];

    NSLog(@"完成");
}

-(void)_produce{
    [condition lock];
     isWait = false;
     [condition signal];
     [condition unlock];
}

輸出結(jié)果:

[13781:212898] 等待條件滿足
[13781:212898] 執(zhí)行操作
[13781:212898] 完成
  • 5、 NSConditionLock 可以使用特定值來加鎖和解鎖,和NSCondition表現(xiàn)差不多。

  • (instancetype)initWithCondition:(NSInteger)condition 參數(shù)condition作為標識符更容易理解,lockWhenCondition獲取指定標記的鎖沒有的話就阻塞當(dāng)前線程,unlockWithCondition:釋放指定標記的鎖,等他的線程獲取鎖然后繼續(xù)執(zhí)行操作。

使用上比NSCondition更方便些,代碼更簡潔。

NSConditionLock改寫以上代碼:

-(void)_testConditionLock{
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _user1];
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _produce1];
    });
}
-(void)_user1{
    NSLog(@"等待條件滿足");
    [conditionLock lockWhenCondition:11];

    NSLog(@"條件滿足了");

    {
        //執(zhí)行操作
        NSLog(@"執(zhí)行操作");
    }

    //完成
    [conditionLock unlockWithCondition:0];

    NSLog(@"完成");
}

[self _testConditionLock];

輸出結(jié)果:

[7812:120141] 等待條件滿足
[7812:120137] 生成條件中...
[7812:120141] 條件滿足了
[7812:120141] 執(zhí)行操作
[7812:120141] 完成
  • 6、 其他不常用的鎖 pthread_mutex pthread_mutex(recursive) POSIX標準的unix多線程,C 語言下多線程實現(xiàn)。

OSSpinLock:自旋鎖,一直輪詢等待時會消耗大量 CPU 資源。

串行隊列

通過創(chuàng)建一個串行隊列,把我們的操作添加到隊列。

dispatch_queue_t queue = dispatch_queue_create("com.queue.test",DISPATCH_QUEUE_SERIAL);

dispatch_async(queue, ^{
    NSLog(@"task 1");
});
dispatch_async(queue, ^{
    NSLog(@"task 2");
});

dispatch_async(queue, ^{
    NSLog(@"task 3");
});

感覺創(chuàng)建隊列、添加操作到隊列太麻煩,不夠簡潔而且隊列的調(diào)度肯定占用不少資源.

GCD

通過dispatch_semaphore信號量實現(xiàn)線程同步

dispatch_semaphore_create(long value);

dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);//-1

dispatch_semaphore_signal(dispatch_semaphore_t dsema);//+1

dispatch_semaphore_wait在信號量為0時會阻塞當(dāng)前線程,等待dispatch_semaphore_signal釋放信號然后繼續(xù)執(zhí)行。

用信號量改寫以上代碼:

-(void)_testSemaphore{
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _user2];
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [weakSelf _produce2];
    });
}
-(void)_user2{
    NSLog(@"等待條件滿足");
    dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);

    NSLog(@"條件滿足了");

    {
        //執(zhí)行操作
        NSLog(@"執(zhí)行操作");
    }

    //完成
    NSLog(@"完成");
}

-(void)_produce2{
    NSLog(@"生成條件中...");
     dispatch_semaphore_signal(semaphore);
}
    semaphore = dispatch_semaphore_create(0);
    NSLog(@"初始化信號0");

    [self _testSemaphore];

多線程并發(fā)的四種方式

ios多線程開發(fā)的常用四種方式 (附有demo)

  1. pthread
  2. NSThread
  3. NSOperation\NSOperationQueue
  4. GCD

一 、pthread

C語言通用的多線程API,跨平臺,程序員手動管理線程生命周期,使用難度大

//創(chuàng)建線程
 NSLog(@"開始執(zhí)行");
 int pthread_create(pthread_t * __restrict ,const pthread_attr_t * __restrict ,void *(*)(void *),void * __restrict);
 //使用
 pthread_t pthread;
 pthread_create(&pthread, NULL, function, NULL);
 NSLog(@"執(zhí)行結(jié)束");

void * function(void * param) {
    for (int i = 0; i < 5; i ++) {
        NSLog(@"i : %d-- 線程: %@",i,[NSThread currentThread]);
    }
    return NULL;
}

二、 NSThread

創(chuàng)建NSThread 有三種方法

//公共方法
- (void)threadMoth {
    NSLog(@"%@",[NSThread currentThread]);
}

// 方法 1 需要手動開啟
NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMoth) object:nil];
[thread start];
//方法 2
[NSThread detachNewThreadSelector:@selector(threadMoth) toTarget:self withObject:nil];
//方法 3
[self performSelectorInBackground:@selector(threadMoth) withObject:nil];

//常用的相關(guān)方法

[NSThread mainThread];// 獲得主線程
[NSThread isMainThread]; //是否為主線程
NSLog(@"shuchu--%@--%d",[NSThread mainThread],[NSThread isMainThread]);
NSLog(@"休眠前");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]];//阻塞線程 以當(dāng)前時間為準阻塞4秒
NSLog(@"休眠1");
[NSThread sleepForTimeInterval:2];//阻塞線程 2 秒
NSLog(@"休眠后");
[NSThread exit];// 強制退出線程

方法2、一調(diào)用就會立即創(chuàng)建一個線程來做事情;
方法1、需要手動調(diào)用 start 啟動線程時才會真正去創(chuàng)建線程。
方法3、是利用NSObject的方法 performSelectorInBackground:withObject: 來創(chuàng)建一個線程:在后臺運行某一個方法
與 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一樣的。

運行結(jié)果

三、NSOperation、NSOperationQueue

NSOperation、NSOperationQueue 是蘋果提供給我們的一套多線程解決方案。實際上 NSOperation、NSOperationQueue 是基于 GCD 更高一層的封裝,完全面向?qū)ο?。但是?GCD 更簡單易用、代碼可讀性也更高。

(1)、NSOperation
NSOperation 是個抽象類,不能用來封裝操作。我們只有使用它的子類來封裝操作。我們有兩種方式來封裝操作。

  • 子類 NSInvocationOperation
  • 子類 NSBlockOperation

1、 子類 NSInvocationOperation

  • 創(chuàng)建 NSInvocationOperation 對象
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget
  • 調(diào)用 start 方法開始執(zhí)行操作
[operation start];
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容