Grand Central Dispatch その7 おまけ

さらに今更ながら、ASCII.jpさんの「マルチコア時代の新機軸! Snow LeopardのGCD」に、わりと一般向けの説明から始まって、コードを使用したプログラマ向けの説明までしているページがありました。とりあえずここらへんで基本知識を得てから、いろいろ試した方が良かったのかもしれません。

その記事を見ていて改めて発見したのは、dispatch_get_main_queueで、メインスレッドで実行されるキューが取得できるという事です。なるほど、これがあればperformSelectorOnMainThreadを呼ばなくてもディスパッチで呼び出しが完結できます。前回のタイマーとかはメインキュー使う方が良い気がします。

でも、こういった解説記事の中でディスパッチソースにまで触れられているものってあんまり見かけないですね。ディスパッチさえあればアプリの基本的な仕組みが作れてしまうみたいな。ということで、ソース系でもうひとつ調べてみました。

やってみたかったのは、ディスパッチでバッチ処理的な事をさせておいている途中で、キャンセルしたいって言う事です。すくなくとも僕の認識では、dispatch_asyncでブロックをキューに渡してしまうとキャンセルできない様なので、これをやりたいとおもって見つけたのがディスパッチソースの中のDISPATCH_SOURCE_TYPE_DATA_ADDというやつです。

それを使って書いてみたソースがこちらです。今回は自分でキャンセルしてみたいので、Cocoa Applicationでプロジェクトを作ってAppDelegateを以下のように変更し、Interface Builderでウィンドウにボタンをひとつ作ってAppDelegateのcancelにつなげてください。

//
// AppDelegate.h
//
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <nsapplicationDelegate> {
    NSWindow *window;
    dispatch_source_t _source;
}
@property (assign) IBOutlet NSWindow *window;
- (IBAction)cancel:(id)sender;
@end
//
// AppDelegate.m
//
#import "AppDelegate.h"
@implementation AppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    dispatch_queue_t queue = dispatch_queue_create("test", NULL);
    _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
    
    __block int count = 0;
    
    dispatch_source_set_event_handler(_source, ^{
        count++;
        printf("call %d\n", count);
        usleep(100000);
        dispatch_source_merge_data(_source, 1);
    });
    
    dispatch_source_set_cancel_handler(_source, ^{
        printf("end\n");
        dispatch_release(_source);
        _source = NULL;
        dispatch_release(queue);
        exit(0);
    });
    
    dispatch_source_merge_data(_source, 1);
    dispatch_resume(_source);
}
- (IBAction)cancel:(id)sender
{
    dispatch_source_cancel(_source);
}
@end

実行すると、event handlerのブロックが繰り返し呼ばれてログにカウントが表示されます。キャンセルボタンを押すと終了します。

DATA_ADDの使い方としては、dispatch_source_merge_data()の2つ目の引数で1以上の値を与えて呼ぶと、dispatch_source_set_event_handlerで登録したブロックが呼び出されるという感じになります。このサンプルでは、event_handlerのブロックの中から直接merge_dataを呼び出して処理を繰り返すようにしています。

merge_dataに0を渡してもブロックが呼ばれない事からも分かるように、たぶん本来はこれで処理自体をするものではなく、こちらのページにのっているように、dispatch_applyとかでやっている処理の進捗の量をmerge_dataの引数で受け取ってインジケータに表示するような用途に使うものだと思われます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です