LoginSignup
0
1

C言語でGrand Central Dispatch(GCD)を使ってみた(その1)

Last updated at Posted at 2023-02-09

本記事について

macOS、iOS、watchOS、tvOSのマルチコアハードウェアにおいて配列でタスクを実行させるための技術、Grand Central Dispatch(GCD)の理解を深めるため、敢えてC言語縛りで使ってみました。
 (嘘です…本当はまだObject-CやSwiftをあまり使ったことがないからです…)

実行環境

・macOS Ventura 13.0.1
・Apple clang version 14.0.0 (clang-1400.0.29.202) 
・lldb-1400.0.38.17
・MacBook Pro 13インチ M2
 ※Homebrew GCCは使えなかったです…

サンプルソース

5秒周期でログを10回出力するソースです。

  1 #include <dispatch/dispatch.h>
  2 #include <stdio.h>
  3 #include <Block.h>
  4 
  5 int main(void)
  6 {
  7         __block int i = 0;
  8         
  9         dispatch_queue_t queue;
 10         dispatch_source_t source;
 11         dispatch_time_t start_time;
 12         
 13         /* 新規ディスパッチキューを作成 */
 14         queue = dispatch_queue_create("jp.test_queue", DISPATCH_QUEUE_SERIAL);
 15         
 16         /* タイマーのディスパッチソースを生成 */
 17         source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 18         
 19         /* イベント発生後に呼ばれるブロックを設定 */
 20         dispatch_source_set_event_handler(source, ^{
 21                 printf("5秒後に起動。%d番目。\n", i);
 22                 i++; 
 23                 if(i == 10){
 24                         printf("実行停止。\n");
 25                         dispatch_suspend(source);
 26                         dispatch_source_cancel(source);
 27                 
 28                         exit(0);
 29                 }
 30         });
 31         
 32         /* タイマーを設定 */
 33         start_time = dispatch_time(DISPATCH_TIME_NOW, 0);
 34         dispatch_source_set_timer(source, start_time, 5.0*NSEC_PER_SEC, 0);
 35         
 36         /* 中断されている新規ディスパッチキューを再開 */
 37         printf("実行開始!\n");
 38         dispatch_resume(source);
 39         
 40         /* 新規ディスパッチキューを実行させるため、メインスレッドを停止 */
 41         dispatch_main();
 42         
 43         return 0;
 44 }

実行結果 

実行開始!
5秒後に起動。0番目。
5秒後に起動。1番目。
5秒後に起動。2番目。
5秒後に起動。3番目。
5秒後に起動。4番目。
5秒後に起動。5番目。
5秒後に起動。6番目。
5秒後に起動。7番目。
5秒後に起動。8番目。
5秒後に起動。9番目。
実行停止。

自分なりの解釈 

まず、下記の関数dispatch_queue_create()にて、新規のディスパッチキューを作成します。
第二引数にDISPATCH_QUEUE_SERIALを指定することにより、シリアルにFIFO 順でブロックを実行するディスパッチキューにすることが出来ます。

 12         
 13         /* 新規ディスパッチキューを作成 */
 14         queue = dispatch_queue_create("jp.test_queue", DISPATCH_QUEUE_SERIAL);
 15  

次に、下記の関数dispatch_source_create()で、第一引数にDISPATCH _SOURCE_TIMER を指定することにより、タイマーに基づいて、新規ディスパッチキューqueueにイベントハンドラブロックを提出するディスパッチソースにを作成します。

 15         
 16         /* タイマーのディスパッチソースを生成 */
 17         source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 18 

ただ、これだけだと
①タイマーの周期はまだ不明瞭。
②タイマー満了後に新規ディスパッチキューに提出されるイベントハンドラブロックは???

という状態になってしまうので、最低でも更に2種類の関数を使用する必要があります。

まずは、関数dispatch_source_set_event_handler()にて、ディスパッチキューに提出されるイベントハンドラブロックを定義します。
(5秒周期のタイマー満了後にメッセージを10回出力。
メッセージを10回出力した後は、そのディスパッチソースを中断・キャンセルし、そのまま終了するという処理を実装。)

 19         /* イベント発生後に呼ばれるブロックを設定 */
 20         dispatch_source_set_event_handler(source, ^{
 21                 printf("5秒後に起動。%d番目。\n", i);
 22                 i++; 
 23                 if(i == 10){
 24                         printf("実行停止。\n");
 25                         dispatch_suspend(source);
 26                         dispatch_source_cancel(source);
 27                 
 28                         exit(0);
 29                 }
 30         });

この後、関数dispatch_source_set_timer()にて、そのタイマーのディスパッチソースのタイマー開始時刻や間隔などを定義します。
(今回はすぐに5秒間隔のタイマー起動という感じに設定しときます。)

 31         
 32         /* タイマーを設定 */
 33         start_time = dispatch_time(DISPATCH_TIME_NOW, 0);
 34         dispatch_source_set_timer(source, start_time, 5.0*NSEC_PER_SEC, 0);
 35  

これであらかたのコーディングは終了なのですが、最後に忘れてはならないのが関数dispatch_resume()です!!!
新規ディスパッチソースは作った段階ではsuspend状態ですので、resumeで発動させなければいけません。

 36         /* 中断されている新規ディスパッチキューを再開 */
 37         printf("実行開始!\n");
 38         dispatch_resume(source);
 39         
 40         /* 新規ディスパッチキューを実行させるため、メインスレッドを停止 */
 41         dispatch_main();
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1