iosマルチスレッド操作-GCD遅延操作と関連使用方法

6474 ワード

iosマルチスレッド操作-GCD遅延操作と関連使用方法
0x01.iOSバージョン
GCD関数を使用すると、遅延操作が可能です.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    });

次に、パラメータdispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)) : NSEC_PER_SECのヘッダファイルでの定義を分解します.#define NSEC_PER_SEC 1000000000ull /* nanoseconds per second */このパラメータは、これからどのくらいのナノ秒dispatch_get_main_queue():が経過したかを示し、メインキュー. ^{ }:がblockタスクを表すことを示します.どのくらいのナノ秒が経過した後、プライマリ・キューによってタスクが非同期で実行されるか、同期で実行されるかをテストできます.コードは次のとおりです.
//  when               
   dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
   
   void (^task)() = ^ {
       //          
       NSLog(@"%@", [NSThread currentThread]);
   }; 
   //       ,            
   dispatch_after(when, dispatch_get_main_queue(), task);
   //        ,       
   NSLog(@"come here");

このことから、プライマリ・キュー・カラムのスケジューリング・タスクが非同期で実行され、実行キューをグローバル・キューとシリアル・キューに変更すると、結果はまったく同じであり、この関数が非同期操作を実行していることがわかる.GCDには、あるコードがプログラム実行中に1回しか実行されないことを保証する関数があります.この関数は次のとおりです.
static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
    })
dispatch_once_tはヘッダファイルで以下のように定義される:typedef long dispatch_once_t;このことから、このタイプはlongタイプであることがわかる.onceTokenが0に等しい場合、blockコードが実行される.dispatch_onceはスレッドが安全で、スレッドの安全に関わる限りロックに関わる.dispatch_onceの内部にもロックがあり、反発ロックよりも性能が高い.この関数を使用して、プログラムの実行中に1つのインスタンスしかなく、外部からアクセスしやすく、インスタンスの個数を制御しやすく、システムリソースを節約することができます.アプリケーションがソースを共有する必要がある場合は、単一のインスタンスモードで実現できます.一例モードはARCとMRCの2つのケースに分けられ,マクロでARC環境であるか否かを判断できる.
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif

ARC環境における単純な一例モード:
@implementation SoundTools
//         ,       
static id instance;
 
//               ,  dispatch_once                   
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}
 
//            
+ (instancetype)sharedSoundTools {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}
 
- (id)copyWithZone:(NSZone *)zone {
    return instance;
}
@end
 
      :
- (void)viewDidLoad {
    [super viewDidLoad];
     
    SoundTools *s1 = [SoundTools sharedSoundTools];
    NSLog(@"%p", s1);
}
 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    SoundTools *s2 = [SoundTools sharedSoundTools];
     
    NSLog(@"%p", s2);
}

2つの方法で印刷されたアドレスはまったく同じです!MRC環境では、次のコードがあります.
//         ,       
static id instance;
 
//               ,  dispatch_once                   
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}
 
//            
+ (instancetype)sharedSoundTools {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}
 
- (id)copyWithZone:(NSZone *)zone {
    return instance;
}
 
#pragma mark - MRC      
/**
                ,             ,           !
 */
//         -1
- (oneway void)release {
    //      , highlight  
}
 
//       +1,        
- (instancetype)retain {
    return instance;
}
 
//           ,    !
- (instancetype)autorelease {
    return instance;
}
 
//                   
- (NSUInteger)retainCount {
    //   :limits.h    CPU            
    return ULONG_MAX;
}

0x02.swift 3.0バージョン
1.実行の遅延:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+3.0, execute: {
         [unowned self] () -> Void in
         //    
     })

PS.DispatchTime対象はnow()で現在時間を取得し、秒数を加えればよい
2.グローバルキューが時間のかかる操作を行った後、メインスレッドに切り替えてUIをリフレッシュする
DispatchQueue.global().async {
         //     
         DispatchQueue.main.async {
             //      UI
         }
     }

3.同期実行操作
     DispatchQueue.global().sync {
         //     
     }

4.キューDispatchQueueを作成するデフォルトの初期化方法は同期キューを作成し、同時キューを作成する場合はattributesで宣言する.concurrent.
     //     
     let serialQueue = DispatchQueue(label: "name")

     //     
     let concurrentQueue = DispatchQueue(label: "name", attributes: .concurrent)

5.複数のタスクを実行した後、ある操作を行います.DispatchGroupを使用します.すべての操作が完了した後、notifyを実行します.
     let group = DispatchGroup()

     let queue1 = DispatchQueue(label: "queue1")
     queue1.async(group: group) {
         //     1
     }
     let queue2 = DispatchQueue(label: "queue2")
     queue1.async(group: group) {
         //     2
     }

     group.notify(queue: DispatchQueue.main) { 
         //     
     }

1つまたは複数のタスクの後に別のタスクを実行する場合は、タスク間に待機を追加します.
     //          
     group.wait(timeout: DispatchTime.distantFuture)

6.DispatchWorkItemの使用
  • DispatchWorkItemは、タスク・エントリとして理解され、デフォルト値があるため、受信優先度などのパラメータを初期化できます.また、閉パケットは1つだけ入力できます.同様にwaitメソッドもあり、使用は上とあまり差がありません.
  •      let queue = DispatchQueue(label: "queue", attributes: .concurrent)
         let workItem = DispatchWorkItem {
             //   
         }
         queue.async(execute: workItem)
         print("before waiting")
         workItem.wait()
         print("after waiting")
    

    7.barrier
  • barrierの参加は、キューに参加する前の「タスク」の実行が完了するまで実行されません.その後、キューの「タスク」に追加すると、この「タスク」の実行が完了するまで実行が開始されません.ここでの「タスク」はDispatchWorkItemで作成されます.
  •      let barrierWorkItem = DispatchWorkItem(flags: .barrier) {
             //     ,       “ ”  ,   “ ”  
         }
         let queue = DispatchQueue(label: "queue", attributes: .concurrent)
         queue.async(execute: barrierWorkItem)
    

    8.信号量
    スレッドの安全な統計数のために、信号量をカウントします.初期化メソッドは1つのみで、1つのIntタイプの数が入力されます.
         let semaphore = DispatchSemaphore(value: 10)
    
         //      
         semaphore.wait()
    
         //      
         semaphore.signal()