【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設定関数」の解析が難しいと思いますので
解析した図を下記に用意しました。
▼処理解説
▼実際の値の流れ
■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