ゆるい感じのプログラムを書きたい。

プログラムの敷居を下げて、多くの人が開発出来るように色々書いていきます!

【iPhoneアプリ】他にもまだまだあった「マルチスレッド」について dispatch_xxxxx編続き

今回もまた、「マルチスレッド」についてです。

前回の「dispatch_xxxxx」について
ですが、記載しきれなかった為、今回は続きを記載します。

続きで記載出来なかったdispatch_xxxxについて

■dispatch_barrier_async

・非同期処理の完了を待つ、もしくは非同期処理の間にblock構文の内容を
 処理したい場合


※処理は非同期で行いますが、前後の処理の整合性を取るため見た目は同期です。

引数 説明
dispatch_queue_t ディスパチキューオブジェクト
dispatch_block_t タスク処理が書かれたblock構文

<注意>
グローバルキュー、プライベートのシリアルキューでは機能しません。
プライベートのコンカレントキューのみ機能します。


こんな感じのプログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{
  dispatch_queue_t queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);

  //非同期処理1
  dispatch_apply(5,queue,^(size_t i){
   NSLog(@"非同期1_処理回数:%d",(int)i);
  });

  //非同期処理2
  dispatch_async(queue,^{
    for(int i=0;i<5;i++){
      NSLog(@"非同期2_処理回数:%d",(int)i);
    }
  });

  //バリアされた非同期処理
  dispatch_barrier_async(queue,^{
    NSLog(@"dispatch_barrier_async");
  });

  //非同期処理3    
  dispatch_apply(5,queue,^(size_t i){
    NSLog(@"非同期3_処理回数:%d",(int)i);
  });
}


結果はこんな感じです。

labo[21289:60b]  非同期1_処理回数:0
labo[21289:60b]  非同期1_処理回数:2
labo[21289:60b]  非同期1_処理回数:3
labo[21289:60b]  非同期1_処理回数:4
labo[21289:1803] 非同期1_処理回数:1
labo[21289:3707] 非同期2_処理回数:0
labo[21289:3707] 非同期2_処理回数:1
labo[21289:3707] 非同期2_処理回数:2
labo[21289:3707] 非同期2_処理回数:3
labo[21289:3707] 非同期2_処理回数:4
labo[21289:3707] dispatch_barrier_async
labo[21289:60b]  非同期3_処理回数:0
labo[21289:60b]  非同期3_処理回数:1
labo[21289:60b]  非同期3_処理回数:2
labo[21289:60b]  非同期3_処理回数:4
labo[21289:1803] 非同期3_処理回数:3

dispatch_barrier_asyncを使うと、先行処理「非同期処理1」「非同期処理2」
終わるまで処理実行を待つ、そして、dispatch_barrier_async内の処理が完了するまで
後続の「非同期処理3」の処理は実行されない。


■dispatch_barrier_async_f

・非同期処理の完了を待つ、もしくは非同期処理の間にblock構文ではなく、関数を
 利用したい場合。


※処理は非同期で行いますが、前後の処理の整合性を取るため見た目は同期です。

引数 説明
dispatch_queue_t ディスパチキューオブジェクト
void * 関数の引数:汎用ポインタ
dispactch_function 関数名

<注意>
グローバルキュー、プライベートのシリアルキューでは機能しません。
プライベートのコンカレントキューのみ機能します。


こんな感じのプログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{
  dispatch_queue_t queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);

  //非同期処理1
  dispatch_apply(5,queue,^(size_t i){
   NSLog(@"非同期1_処理回数:%d",(int)i);
  });

  //非同期処理2
  dispatch_async(queue,^{
    for(int i=0;i<5;i++){
      NSLog(@"非同期2_処理回数:%d",(int)i);
    }
  });

  //バリアされた非同期処理
  dispatch_barrier_async_f(queue,(void *)int_obj,barrier_method);

  //非同期処理3    
  dispatch_apply(5,queue,^(size_t i){
    NSLog(@"非同期3_処理回数:%d",(int)i);
  });
}

//呼び出すメソッド
void barrier_method(void *context)
{
  NSLog(@"引数のvoid_ポインタ値:%d",(int)context);
}


結果はこんな感じです。

labo[21326:60b]  非同期1_処理回数:0
labo[21326:1803] 非同期1_処理回数:1
labo[21326:60b]  非同期1_処理回数:2
labo[21326:1803] 非同期1_処理回数:3
labo[21326:60b]  非同期1_処理回数:4
labo[21326:1803] 非同期2_処理回数:0
labo[21326:1803] 非同期2_処理回数:1
labo[21326:1803] 非同期2_処理回数:2
labo[21326:1803] 非同期2_処理回数:3
labo[21326:1803] 非同期2_処理回数:4
labo[21326:1803] 引数のvoid_ポインタ値:20
labo[21326:60b]  非同期3_処理回数:0
labo[21326:1803] 非同期3_処理回数:1
labo[21326:1803] 非同期3_処理回数:3
labo[21326:1803] 非同期3_処理回数:4
labo[21326:60b]  非同期3_処理回数:2

dispatch_barrier_async_fを使うと、先行処理「非同期処理1」「非同期処理2」
終わるまで処理実行を待つ、そして、dispatch_barrier_async内の処理が完了するまで
後続の「非同期処理3」の処理は実行されない。


■dispatch_barrier_sync

・非同期処理の完了を待つ、もしくは非同期処理の間にblock構文の処理をする場合。


※処理は同期で行います。

引数 説明
dispatch_queue_t ディスパチキューオブジェクト
dispatch_block_t タスク処理が書かれたblock構文

<注意>
グローバルキュー、プライベートのシリアルキューでは機能しません。
プライベートのコンカレントキューのみ機能します。


こんな感じのプログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{
  dispatch_queue_t queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);

  //非同期処理1
  dispatch_apply(5,queue,^(size_t i){
   NSLog(@"非同期1_処理回数:%d",(int)i);
  });

  //非同期処理2
  dispatch_async(queue,^{
    for(int i=0;i<5;i++){
      NSLog(@"非同期2_処理回数:%d",(int)i);
    }
  });

  //バリアされた非同期処理
  dispatch_barrier_sync(queue,^{
    NSLog(@"dispatch_barrier_async");
  });

  //非同期処理3    
  dispatch_apply(5,queue,^(size_t i){
    NSLog(@"非同期3_処理回数:%d",(int)i);
  });
}


結果はこんな感じです。

labo[21348:1803] 非同期1_処理回数:1
labo[21348:60b]  非同期1_処理回数:0
labo[21348:60b]  非同期1_処理回数:3
labo[21348:1803] 非同期1_処理回数:2
labo[21348:60b]  非同期1_処理回数:4
labo[21348:1803] 非同期2_処理回数:0
labo[21348:1803] 非同期2_処理回数:1
labo[21348:1803] 非同期2_処理回数:2
labo[21348:1803] 非同期2_処理回数:3
labo[21348:1803] 非同期2_処理回数:4
labo[21348:60b]  dispatch_barrier_async
labo[21348:60b]  非同期3_処理回数:0
labo[21348:60b]  非同期3_処理回数:2
labo[21348:60b]  非同期3_処理回数:3
labo[21348:1803] 非同期3_処理回数:1
labo[21348:60b]  非同期3_処理回数:4

dispatch_barrier_sync_fを使うと、先行処理「非同期処理1」「非同期処理2」が
終わるまで処理実行を待つ、そして、dispatch_barrier_async内の処理が完了するまで
後続の「非同期処理3」の処理は実行されない。


■dispatch_barrier_sync_f

・非同期処理の完了を待つ、もしくは非同期処理の間にblock構文ではなく、関数を
 利用したい場合。


※処理は同期で行います。

引数 説明
dispatch_queue_t ディスパチキューオブジェクト
void * 関数の引数:汎用ポインタ
dispactch_function 関数名

<注意>
グローバルキュー、プライベートのシリアルキューでは機能しません。
プライベートのコンカレントキューのみ機能します。


こんな感じのプログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{
  dispatch_queue_t queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);

  //非同期処理1
  dispatch_apply(5,queue,^(size_t i){
   NSLog(@"非同期1_処理回数:%d",(int)i);
  });

  //非同期処理2
  dispatch_async(queue,^{
    for(int i=0;i<5;i++){
      NSLog(@"非同期2_処理回数:%d",(int)i);
    }
  });

  //バリアされた非同期処理
  dispatch_barrier_async_f(queue,(void *)int_obj,barrier_method);

  //非同期処理3    
  dispatch_apply(5,queue,^(size_t i){
    NSLog(@"非同期3_処理回数:%d",(int)i);
  });
}

//呼び出すメソッド
void barrier_method(void *context)
{
  NSLog(@"引数のvoid_ポインタ値:%d",(int)context);
}


結果はこんな感じです。

labo[21358:60b]  非同期1_処理回数:0
labo[21358:1803] 非同期1_処理回数:1
labo[21358:1803] 非同期1_処理回数:2
labo[21358:60b]  非同期1_処理回数:4
labo[21358:1803] 非同期1_処理回数:3
labo[21358:1803] 非同期2_処理回数:0
labo[21358:1803] 非同期2_処理回数:1
labo[21358:1803] 非同期2_処理回数:2
labo[21358:1803] 非同期2_処理回数:3
labo[21358:1803] 非同期2_処理回数:4
labo[21358:60b]  引数のvoid_ポインタ値:20
labo[21358:60b]  非同期3_処理回数:0
labo[21358:1803] 非同期3_処理回数:1
labo[21358:1803] 非同期3_処理回数:3
labo[21358:60b]  非同期3_処理回数:2
labo[21358:1803] 非同期3_処理回数:4

dispatch_barrier_sync_fを使うと、先行処理「非同期処理1」「非同期処理2」
終わるまで処理実行を待つ、そして、dispatch_barrier_async内の処理が完了するまで
後続の「非同期処理3」の処理は実行されない。


■dispatch_suspend

・キューの処理を一時的に中断させる。

<注意>
プライベートのシリアルキュー、コンカレントキューのみ機能します。


こんな感じのプログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{

  dispatch_queue_t con_queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);
 
  dispatch_apply(5,con_queue,^(size_t i){
   NSLog(@"非同期1_処理回数:%d",(int)i);
  });

  //con_queueを一時的に停止
  dispatch_suspend(con_queue);
   
  dispatch_apply(5,con_queue,^(size_t i){
    NSLog(@"非同期2_処理回数:%d",(int)i);
  });
}


結果はこんな感じです。

labo[21465:60b]  非同期1_処理回数:0
labo[21465:1803] 非同期1_処理回数:1
labo[21465:1803] 非同期1_処理回数:3
labo[21465:60b]  非同期1_処理回数:2
labo[21465:1803] 非同期1_処理回数:4

dispatch_suspendがの処理以降は、「con_queue」キューが一時停止されているため
処理がされていない。


また、シリアルキューコンカレント2つのキューを組み合わせて
処理を行ってみる。


こんな感じのプログラムを記載します。

- (IBAction)thread_Button:(UIButton*)sender
{

  dispatch_queue_t con_queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);
  dispatch_queue_t sri_queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_SERIAL);
    
  dispatch_apply(5,con_queue,^(size_t i){
    NSLog(@"非同期1_処理回数:%d",(int)i);
  });

  //con_queueを一時的に停止
  dispatch_suspend(con_queue);
   
  dispatch_apply(5,sri_queue,^(size_t i){
    NSLog(@"非同期4_処理回数:%d",(int)i);
  });
    
	//con_queueを再開
  dispatch_resume(con_queue);


結果はこんな感じです。

labo[21499:60b]  非同期1_処理回数:0
labo[21499:60b]  非同期1_処理回数:2
labo[21499:60b]  非同期1_処理回数:3
labo[21499:60b]  非同期1_処理回数:4
labo[21499:1803] 非同期1_処理回数:1
labo[21499:60b]  非同期4_処理回数:0
labo[21499:60b]  非同期4_処理回数:1
labo[21499:60b]  非同期4_処理回数:2
labo[21499:60b]  非同期4_処理回数:3
labo[21499:60b]  非同期4_処理回数:4

dispatch_suspendがの処理以降は、「con_queue」キューが一時停止されているが
次の処理が「sri_queue」で別キューの為、処理が継続される。


■dispatch_suspend

・一時的に中断させていた、キューを再開させる。

<注意>
プライベートのシリアルキュー、コンカレントキューのみ機能します。


こんな感じのプログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{
   dispatch_queue_t con_queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);

   dispatch_apply(5,con_queue,^(size_t i){
     NSLog(@"非同期1_処理回数:%d",(int)i);
   });

   //con_queueを一時的に停止
   dispatch_suspend(con_queue);

   //mainキュー処理
   for(int i=0;i<5;i++){
     NSLog(@"main_処理回数:%d",(int)i);
   }

   //con_queueを再開
   dispatch_resume(con_queue);

   dispatch_apply(5,con_queue,^(size_t i){
     NSLog(@"非同期2_処理回数:%d",(int)i);
   });


結果はこんな感じです。

labo[21508:60b]  非同期1_処理回数:0
labo[21508:1803] 非同期1_処理回数:1
labo[21508:60b]  非同期1_処理回数:2
labo[21508:1803] 非同期1_処理回数:3
labo[21508:60b]  非同期1_処理回数:4
labo[21508:60b]  main_処理回数:0
labo[21508:60b]  main_処理回数:1
labo[21508:60b]  main_処理回数:2
labo[21508:60b]  main_処理回数:3
labo[21508:60b]  main_処理回数:4
labo[21508:60b]  非同期2_処理回数:0
labo[21508:60b]  非同期2_処理回数:2
labo[21508:60b]  非同期2_処理回数:3
labo[21508:60b]  非同期2_処理回数:4
labo[21508:1803] 非同期2_処理回数:1

dispatch_suspendがの処理以降は、「con_queue」キューが停止され
「main」キューの処理後dispatch_resume「con_queue」キューの処理が再開される。


■dispatch_semaphore

・非同期処理から同期処理を行いたい場合に利用する。


▼dispatch_semaphore_create()

・初期値を使用して新しい「CountingSemaphore」を作成します。

型名 説明
long値 カウントの数


▼dispatch_semaphore_signal

・「CountingSemaphore」をインクリメント(増やす)します。

型名 説明
dispatch_semaphore_t カウンティングセマフォ


▼dispatch_semaphore_wait

・「CountingSemaphore」をデクリメント(減らす)します。

型名 説明
dispatch_semaphore_t カウンティングセマフォ
dispatch_time_t タイムアウト時間設定


<注意>
グローバルキュー、プライベートのシリアルキュー、コンカレントキューのみ
機能します。


こんな感じのプログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{
   dispatch_queue_t con_queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);

   dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
   
	 dispatch_async(sri_queue, ^{
     //async内の処理開始
     for (int i = 1; i <= 5; i++) {
       NSLog(@"非同期_async内処理_%d:%p",i,[NSThread currentThread]);
     }
     //非同期処理完了
		 dispatch_semaphore_signal(semaphore);
   }

   NSLog(@"非同期_async外処理:%p",[NSThread currentThread]);
   //非同期処理終了待ち
   dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
   
   NSLog(@"同期_メイン処理:%p",[NSThread currentThread]);

}


結果はこんな感じです。

20:54:39.125 labo[22427:60b]  非同期_async外処理:0x14556c60
20:54:39.126 labo[22427:1803] 非同期_async内処理_1:0x1465b080
20:54:39.126 labo[22427:1803] 非同期_async内処理_2:0x1465b080
20:54:39.128 labo[22427:1803] 非同期_async内処理_3:0x1465b080
20:54:39.129 labo[22427:1803] 非同期_async内処理_4:0x1465b080
20:54:39.130 labo[22427:1803] 非同期_async内処理_5:0x1465b080
20:54:39.131 labo[22427:60b]  同期_メイン処理:0x14556c60

ログを見ると「非同期_async内処理」「非同期_async外処理」両方の処理が終わるまで
同期_メイン処理」が待っているため、非同期処理から同期処理が可能となります。


■どのように非同期から同期になっているのか


プログラム解説

f:id:kassans:20140328140059p:plain


動作のイメージ

f:id:kassans:20140328140118p:plain


dispatch_semaphoreを複数用いた場合のイメージ

f:id:kassans:20140328140207p:plain

ちなみに、参考に「dispatch_semaphore」を利用しない場合は、以下のような
感じになります。


プログラムはこんな感じです。

- (IBAction)thread_Button:(UIButton*)sender
{
   dispatch_queue_t con_queue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_CONCURRENT);

   dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
     dispatch_async(sri_queue, ^{
       for (int i = 1; i <= 5; i++) {
         NSLog(@"非同期_async内処理_%d:%p",i,[NSThread currentThread]);
       }
     });
    
   NSLog(@"非同期_async外処理:%p",[NSThread currentThread]);
   NSLog(@"同期_メイン処理:%p",[NSThread currentThread]);


結果はこんな感じです。

labo[22733:60b]  非同期_async外処理:0x14e525b0
labo[22733:1803] 非同期_async内処理_1:0x14d7dec0
labo[22733:60b]  同期_メイン処理:0x14e525b0
labo[22733:1803] 非同期_async内処理_2:0x14d7dec0
labo[22733:1803] 非同期_async内処理_3:0x14d7dec0
labo[22733:1803] 非同期_async内処理_4:0x14d7dec0
labo[22733:1803] 非同期_async内処理_5:0x14d7dec0

上記の結果、非同期処理を待たずに同期処理がされているのが
わかると思います。


■スレッド処理作成時に使えるログ出力方法

NSObjectの「NSThread」の以下のメソッドを使います。


▼isMainThread
メインスレッドを利用しているのかを確認します。

・記載例

NSLog(@"MainThread使用中:%d", [NSThread isMainThread]);

・ログ

MainThread使用中:1
//0:未使用
//1:利用中


▼isMultiThreaded
マルチスレッドを利用しているのかを確認します。

・記載例

NSLog(@"MainThread使用中:%d", [NSThread isMainThread]);

・ログ

MultiThread使用中:1
//0:未使用
//1:利用中


▼currentThread
現在使用中のスレッドオブジェクトのアドレス値が返ってきます。

・記載例

NSLog(@"スレッドオブジェクト値:%p",[NSThread currentThread]);

・ログ

スレッドオブジェクト値:0x15574c70