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

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

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

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

以前は「概要編」「実装編」として、基本的な使用法を記載致しましたが
今回は、その他の「dispatch_xxxxx」について記載します。

こちらも「dispatch_xxxxx」「dispatch_group_xxxxx」と2部構成になります。

前回、記載出来なかったdispatch_xxxxについて

サンプルソース
前回の【iPhoneアプリ】これを使えるようにならないと「マルチスレッド」について 実装編で
記載した「//スレッドの処理をこの部分に記載します。」部分に記載してください。

■dispatch_async_f

・非同期のタスク処理をblock構文ではなく、関数を利用したい場合

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


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

- (IBAction)thread_Button:(UIButton*)sender
{
  int int_obj = 20;
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  //dispatch_async_f実行
  dispatch_async_f(queue,(void *)int_obj,method_object);
}

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


結果はこんな感じです。

labo[19985:3d07] 引数のvoid_ポインタ値:20


■dispatch_sync_f

・同期のタスク処理をblock構文ではなく、関数を利用したい場合

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


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

- (IBAction)thread_Button:(UIButton*)sender
{
  int int_obj = 20;
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  //dispatch_sync_f実行
  dispatch_sync_f(queue,(void *)int_obj,method_object);
}

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


結果はこんな感じです。

labo[19994:60b] 引数のvoid_ポインタ値:20


ちなみに、下記のようにchar型の配列で処理を行うと
dispatch_async_f」「dispatch_sync_f」とでは表示が異なります。

そのため、「dispatch_async_f」の方では文字化けが起きるのでchar型の配列
使わない方がいいと思います。


こんな感じのchar型配列の処理プログラムを書きます。

- (IBAction)thread_Button:(UIButton*)sender
{
  char   char_arr[]  = "ABCDE";
    
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  //dispatch_async_f実行
  dispatch_async_f(queue,void_int,method_object);
  //dispatch_sync_f実行
  dispatch_sync_f(queue,void_int,method_object);
}


結果はこんな感じです。

labo[20044:1803] async_f 引数のvoid_ポインタ値:P΂ //文字化け発生!!
labo[20044:60b]  sync_f  引数のvoid_ポインタ値:ABCDE


■dispatch_after

・指定した時刻にblock構文によるタスク処理を実行させる場合

引数 説明
dispatch_time_t dispatch_timeもしくはdispatch_walltimeのオブジェクトを指定
dispatch_queue_t ディスパチキューオブジェクト
dispatch_block_t タスク処理が書かれたblock構文


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

NSLog(@"タスク実行前");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,5*NSEC_PER_SEC),queue,^(void){
  NSLog(@"タスク実行後:呼び出されてから5秒後に処理されました。");
});


結果はこんな感じです。

19:22:07.113 labo[18333:60b] タスク実行前
19:22:12.576 labo[18333:3707] タスク実行後:呼び出されてから5秒後に処理されました。


■dispatch_after_f

・指定した時刻に関数を利用したい場合

引数 説明
dispatch_time_t dispatch_timeもしくはdispatch_walltimeオブジェクトを指定
(下記詳細)
dispatch_queue_t ディスパチキューオブジェクト
void * 関数の引数:汎用ポインタ
dispactch_function 関数名


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

- (IBAction)thread_Button:(UIButton*)sender
{
	 int int_obj = 20;
    
   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW,5 * NSEC_PER_SEC),queue,(void *)int_obj,method_object);

}

//呼び出すメソッド
void method_object(void *context)
{
    NSLog(@"タスク実行後:呼び出されてから5秒後に処理されました。");
    NSLog(@"引数のvoid_ポインタ値:%d",(int)context);
}


結果はこんな感じです。

19:56:40.632 labo[20020:1803] タスク実行後:呼び出されてから5秒後に処理されました。
19:56:40.634 labo[20020:1803] 引数のvoid_ポインタ値:20


▼dispatch_time

・dispatch_t型の指定した値からナノ秒単位の経過した時間を返します。

引数 説明
when dispatch_time_t型(unit64_t型)の値を指定
(下記詳細)
delta 待ち時間をdispatch_time_t 型(unit64_t型)を使ってナノ秒単位で指定


・dispatch_time_t 型 (uint64_t 型)

定数名 意味 利用方法
DISPATCH_TIME_NOW 現在時間 dispatch_timeのwhenなど、開始時間を指定時に利用
DISPATCH_TIME_FOREVER 時間制限無し dispatch_timeのdeltaなどで待ち時間を無制限時に利用
NSEC_PER_SEC 1秒あたりのナノ秒 1 秒として、目的のナノ秒数を算出します。例:5秒であれば、「NSEC_PER_SEC * 5」、0.5秒であれば「NSEC_PER_SEC / 2.0」と指定
NSEC_PER_MSEC 1ミリ秒あたりのナノ秒 1 ミリ秒として、目的のナノ秒を算出します。例:5ミリ秒であれば「NSEC_PER_MSEC * 5"」0.5 ミリ秒であれば「NSEC_PER_MSEC / 2.0」と指定
NSEC_PER_SEC 1秒あたりのマイクロ秒 1 秒として、目的のマイクロ秒を指定
NSEC_PER_USEC 1マイクロ秒あたりのナノ秒 1 マイクロ秒として、目的のナノ秒を算出します。例:5 マイクロ秒であれば「NSEC_PER_USEC * 5」0.5 マイクロ秒であれば「NSEC_PER_USEC / 2.0」と指定


▼dispatch_walltime

・「clock_gettime」の「timespec」に指定した時間からナノ秒単位の経過した
 時間を返します。

 ※もし、引数「*when」がNullの場合、gettimeofdayのが利用されます。
  実際に使ったところ、現在時刻でした。


引数 説明
*when struct timespecの値を指定
delta 待ち時間をdispatch_time_t 型(unit64_t型)を使ってナノ秒単位で指定


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

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

  NSDate *date = [NSDate date];
  NSLog(@"TestThread_処理開始前");

  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  dispatch_after(getDispatchTimebyDates(date),queue,^{
   NSLog(@"TestThread_block処理開始");
  });

}

//walltime設定関数
dispatch_time_t getDispatchTimebyDates(NSDate *date)
{
    
   NSTimeInterval interval;
   double second,subsecond;
   struct timespec time;
   dispatch_time_t milestone;
    
   interval = [date timeIntervalSince1970];
   subsecond = modf(interval,&second);

   time.tv_sec = second;
   time.tv_nsec = subsecond * NSEC_PER_SEC;
    
   //dispatch_walltimeに設定
   milestone = dispatch_walltime(NULL,NSEC_PER_SEC*5);
   //dispatch_walltimeを返す。
   return milestone;
}


結果はこんな感じです。

  labo[20272:60b] TestThread_処理開始前
  labo[20272:1807] TestThread_block処理開始


walltime設定関数」の解析が難しいと思いますので
解析した図を下記に用意しました。

▼処理解説

f:id:kassans:20140325160108p:plain

▼実際の値の流れ

f:id:kassans:20140325160708p:plain


■dispatch_apply

・指定した回数だけblock構文の処理を繰り返す。

引数 説明
iterations 繰り返す回数を指定
dispatch_queue_t ディスパチキューオブジェクト
dispatch_block_t タスク処理が書かれたblock構文、引数に現在のiterations回数が代入される


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

- (IBAction)thread_Button:(UIButton*)sender
{
   //レキシカルスコープ
   __block int i = 0;
   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   dispatch_apply(10,queue,^(size_t count){
      i = count;
      NSLog(@"dispatch_apply実行回数:%d",i);
   });
}


結果はこんな感じです。

labo[20404:60b]  dispatch_apply実行回数:0
labo[20404:1803] dispatch_apply実行回数:1
labo[20404:1803] dispatch_apply実行回数:3
labo[20404:60b]  dispatch_apply実行回数:2
labo[20404:1803] dispatch_apply実行回数:4
labo[20404:60b]  dispatch_apply実行回数:5
labo[20404:1803] dispatch_apply実行回数:6
labo[20404:60b]  dispatch_apply実行回数:7
labo[20404:1803] dispatch_apply実行回数:8
labo[20404:60b]  dispatch_apply実行回数:9

順番には処理されないが、asyncと同じではなく
メインスレッドと別スレッドで並列処理がされています。



■dispatch_apply_f

・指定した回数だけ、指定の関数の処理を繰り返す。

引数 説明
iterations 繰り返す回数を指定
dispatch_queue_t ディスパチキューオブジェクト
void * 関数の引数:汎用ポインタ
dispactch_function 関数名

※指定した関数の引数に、汎用ポインタ以外に現在のiterations回数が代入される


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

- (IBAction)thread_Button:(UIButton*)sender
{
    int int_obj = 20;
    dispatch_apply_f(10,queue,(void *)int_obj,apply_method);
}

void apply_method(void *context,size_t i)
{
    NSLog(@"引数のvoid_ポインタ値:%d",(int)context);
    NSLog(@"dispatch_apply実行回数:%d",(int)i);
}


結果はこんな感じです。

labo[20426:1803] 引数のvoid_ポインタ値:20
labo[20426:60b]  引数のvoid_ポインタ値:20
labo[20426:60b]  dispatch_apply実行回数:0
labo[20426:1803] dispatch_apply実行回数:1
labo[20426:60b]  引数のvoid_ポインタ値:20
labo[20426:60b]  dispatch_apply実行回数:2
labo[20426:60b]  引数のvoid_ポインタ値:20
labo[20426:60b]  dispatch_apply実行回数:4
labo[20426:1803] 引数のvoid_ポインタ値:20
labo[20426:1803] dispatch_apply実行回数:3
labo[20426:60b]  引数のvoid_ポインタ値:20
labo[20426:1803] 引数のvoid_ポインタ値:20
labo[20426:60b]  dispatch_apply実行回数:5
labo[20426:60b]  引数のvoid_ポインタ値:20
labo[20426:1803] dispatch_apply実行回数:6
labo[20426:1803] 引数のvoid_ポインタ値:20
labo[20426:60b]  dispatch_apply実行回数:7
labo[20426:1803] dispatch_apply実行回数:8
labo[20426:60b]  引数のvoid_ポインタ値:20
labo[20426:60b]  dispatch_apply実行回数:9

順番には処理されないが、asyncと同じではなく
メインスレッドと別スレッドで並列処理がされています。


■dispatch_once

・記載したblock構文を一度だけ実行する。

引数 説明
*predicate dispatch_once_tのポインタ
block タスク処理が書かれたblock構文


▼dispatch_once_t

・dispatch_onceで利用するオブジェクト
必ずstaicもしくは、グローバルでの変数を作成する。


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

  for(int i=0;i<10;i++){
    NSLog(@"処理回数:%d",i);
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      NSLog(@"dispatch_once実行");
    });
  }
}


結果はこんな感じです。

labo[20456:60b] 処理回数:0
labo[20456:60b] dispatch_once実行
labo[20456:60b] 処理回数:1
labo[20456:60b] 処理回数:2
labo[20456:60b] 処理回数:3
labo[20456:60b] 処理回数:4
labo[20456:60b] 処理回数:5
labo[20456:60b] 処理回数:6
labo[20456:60b] 処理回数:7
labo[20456:60b] 処理回数:8
labo[20456:60b] 処理回数:9