【原】iOS多線程之線程間通信和線程互斥


線程間通信

 1> 線程間通信分為兩種

  • 主線程進入子線程(前面的方法都可以)

  • 子線程回到主線程

 2> 返回主線程

 3> 代碼

  這個案例的思路是:當我觸摸屏幕時,會在子線程加載圖片,然后在主線程刷新UI界面

  視圖布局我就不寫了,大家自己來吧,線程間通信代碼如下:

#pragma mark - 添加響應方法觸發創建子線程並加載數據
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 創建子線程
    [self performSelectorInBackground:@selector(loadImage) withObject:nil];
}

- (void)loadImage
{
   NSLog(@"當前線程:%@", [NSThread currentThread]);
   NSLog(@"主線程:%@", [NSThread mainThread]);
NSString
*urlStr = @"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1463455875&di=ef2da3f0fe711b471966aa1511483d0b&src=http://img4.duitang.com/uploads/item/201308/20/20130820094450_rsmYi.jpeg"; NSURL *url = [NSURL URLWithString:urlStr]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; self.imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; // 返回主線程,刷新UI [self performSelectorOnMainThread:@selector(reloadImageView) withObject:nil waitUntilDone:YES]; } - (void)reloadImageView { // 進入主線程一般進行來安全判斷 if ([NSThread isMainThread]) { // 刷新UI UIImage *showIamge = [UIImage imageWithData:self.imageData]; self.showImageView.image = showIamge; } }

點擊觸發事件,開辟子線程成功,在主線程中刷新UI,圖片顯示成功

線程互斥

 1> 應用場景

  多線程並行編程中,線程間同步與互斥是一個很有技巧的也很容易出錯的地方。

  多個線程操作同一個資源(即某個對象),需要保證線程在對資源的狀態(即對象的成員變量)進行一些非原子性操作后,狀態仍然正確。

  典型的例子是“售票廳售票應用”。售票廳剩余20張票,10個窗口去賣這些票。這10個窗口,就是10條線程,售票廳就是他們共同操作的資源,其中剩余的20張票就是這個資源的一個狀態。線程買票的過程就是去遞減這個剩余數量的過程。

  我們看看會發生什么問題

- (void)viewDidLoad {
    [super viewDidLoad];
    // 模擬買票系統
    // 一共20張票,10個窗口賣
    __block NSInteger count = 20;
    dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(ticketQueue, ^{
            //這里相當於每個窗口賣2張票
            for (int i = 0; i < 2; i ++) {
                NSLog(@"買到了第%ld張票",count);
                count--;
            }
        });
    }
}

運行的效果如下圖,不同的售票窗口販賣了同一張

 2> 線程互斥解決方案

  • 方法一 @synchronized 自動對參數對象加鎖,保證臨界區內的代碼線程安全(最簡單的方法)

  官方文檔解釋:

   The @synchronized directive is a convenient way to create mutex locks on the fly in Objective-C code.

  個人理解:

   @synchronized, 代表這個方法加鎖, 相當於不管哪一個線程(例如線程A),運行到這個方法時,都要檢查有沒有其它線程例如B正在用這個方法,有的話要等正在使用synchronized方法 的線程B運行完這個方法后再運行此線程A,沒有的話,直接運行。它包括兩種用法:synchronized 方法和 synchronized 塊。

   @synchronized 方法控制對類(一般在IOS中用在單例中)的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法鎖方能執行,否則所屬就會發生線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該 鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類,至多只有一個處於可執行狀態,從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類的 方法均被聲明為 synchronized)

- (void)viewDidLoad {
    [super viewDidLoad];
    // 模擬買票系統
    // 一共20張票,10個窗口賣
    __block NSInteger count = 20;
    __weak typeof(self) weakSelf = self;
    dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(ticketQueue, ^{
            // 給買票操作加鎖,保證代碼塊只有一個線程訪問
            @synchronized(weakSelf) {
                //這里相當於每個窗口賣2張票
                for (int i = 0; i < 2; i ++) {
                    NSLog(@"買到了第%ld張票",count);
                    count--;
                }
            }
        });
    }
}

  運行結果:

  • 方法二 NSLock

  官方文檔解釋:

   An NSLock object is used to coordinate the operation of multiple threads of execution within the same application. An NSLock object can be used to mediate access to an application’s global data or to protect a critical section of code, allowing it to run atomically.

  個人理解:

   在一個應用里面協調線程間的執行。

- (void)viewDidLoad {
    [super viewDidLoad];
    // 模擬買票系統
    // 一共20張票,10個窗口賣
    __block NSInteger count = 20;
    
    // 創建線程鎖
    NSLock *lock = [[NSLock alloc]init];
    dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(ticketQueue, ^{
            // 給買票操作加鎖,保證代碼塊只有一個線程訪問
            // 加鎖,不會出現多個窗口同時賣一張票的情況
            [lock lock];
            //這里相當於每個窗口賣2張票
            for (int i = 0; i < 2; i ++) {
                NSLog(@"買到了第%ld張票",count);
                count--;
            }
            // 解鎖
            [lock unlock];
        });
    }
}

  以上兩種方法小編進行了使用,以下兩種方法大家有興趣可以試試,小編就不進行使用了

  • 方法三 NSConditionLock 條件鎖 可以設置條件

  官方文檔解釋:

   The NSConditionLock class defines objects whose locks can be associated with specific, user-defined conditions. Using an NSConditionLock object, you can ensure that a thread can acquire a lock only if a certain condition is met. Once it has acquired the lock and executed the critical section of code, the thread can relinquish the lock and set the associated condition to something new. The conditions themselves are arbitrary: you define them as needed for your application.

  個人理解:

   根據條件加鎖與解鎖。

  • 方法四 NSRecursiveLock 遞歸鎖 多次調用不會阻塞已獲取該鎖的線程

  官方文檔解釋:

   NSRecursiveLock defines a lock that may be acquired multiple times by the same thread without causing a deadlock, a situation where a thread is permanently blocked waiting for itself to relinquish a lock. While the locking thread has one or more locks, all other threads are prevented from accessing the code protected by the lock.

  個人理解:

    同一個線程可以多次請求加鎖,但不會引起死鎖。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM