このページの本文へ

もっと知りたい! Snow Leopard 第4回

Snow Leopardの深層・その2

マルチコア時代の新機軸! Snow LeopardのGCD

2009年09月02日 18時15分更新

文● 千種菊里

  • この記事をはてなブックマークに追加
  • 本文印刷

GDCを使ったプログラミング

空いたスレッドを自動的に再利用

 GCDはエンジンが優れているだけではない。とにかく使うのが楽なのだ。では、試しにGCDを利用してみよう。

 下記のリストは、簡単なサンプルプログラムだ。10周のループがあり、1周回るごとに「3秒待ってメッセージを出力する」というタスクを1回実行する。

 実行結果はリストの下にある通り。1周回るたびにタスクそのもので3秒、ループの待ち時間で1秒の合計4秒を使い、40秒かかって実行されているのが分かる。


 これを、GCDで並列化したのが下のリストだ。

 このソースでは、dispatch_get_global_queueで優先順位「標準」のキューを取得して、dispatch_asyncでブロックをこのキューに登録。即座に処理を続行するという流れだ。ブロックの実行と並列してこのループを繰り返えすので、10個のブロックが1秒おきに次々とキューに投入される。

 キューはできるだけ並列にブロックを処理しようとするが、ここでは1秒の待ち時間があるので、投入が1秒おきとなる。先ほどのリストでは、処理にかかる3秒とループの待ち時間1秒の合計4秒ごとに結果を出力していた。このリストでは3秒の処理をブロックとしているため、並列化すると、下の結果のように4秒後から1秒おきに出力される。

 ただし、下の結果を見ると、7回目までの出力しか表示されていない。これは7回目が出力されたところで10周のループをすべて済ませてしまい、プログラムそのものが終了してしまうからだ。

 この途中での終了を回避するには、ブロックをグループにまとめて、そのグループのすべてのブロックが終わるまで待たせる、という処理を付け加える必要がある。

 下のリストではdispatch_asyncの代わりにdispatch_group_asyncを使うことで、ブロックのキュー登録と同時に、グループに付け加えるという処理を行なっている。

 そして、ループを抜けたあとのdispatch_group_waitで、グループ上のブロックがすべて処理されるまで待たせている。これでループを抜けたからといって、即座にプログラムが終了してしまうことがなくなる。

 最初に出した単なるループのリストと、dispatch_asyncやdispatch_group_asyncを使ったリストの実行結果を比べてみると、1秒ごとに出力しているthreadの値が異なっていることが分かる。

 プログラムを確認すると、このthreadの値は、自分のスレッドを返すpthread_selfの戻り値を出力している。要するに、値が同じならば同じスレッドで、異なるなら別のスレッドで処理を行なったということだ。

 このthreadの値を元に判断すると、最初のリストではまったく並列処理がされていないが、dispatch_asyncやdispatch_group_asyncを使ったリストでは別スレッドで並列に処理していることが分かる。

 さらに1周目(count 0)と4周目(count 3)のブロックでthreadの値が同じ、つまり同じスレッドで実行していることも確認できる。GCDでは単純にスレッドを次々生成するだけでなく、空いたスレッドがあれば自動的にそれを再利用してくれるのだ。

 ソースコードを字面としてざっと眺めていただければ分かるが、要するに並列化に必要なのは単に「処理をブロックに分ける」と「dispatch_asyncやdispatch_group_asyncでくくる」という2点だけだ。単なる並列化だけならこれで十分で、pthreadライブラリによるマルチスレッドプログラミングに比べると非常に平易である。

カテゴリートップへ

この連載の記事

ASCII.jp RSS2.0 配信中