LoginSignup
4
2

More than 5 years have passed since last update.

Objective-Cでマルチスレッドデザインパターンを実装してみた ~Single Threaded Execution~

Last updated at Posted at 2018-09-10

はじめに

Java言語のマルチスレッドデザインパターンのObjective-C言語版がないかと探してみましたが、見つからなったので自分で作成してみました。

はじめに Single Threaded Execution パターンを実装してみます。

非同期の実装方法は、Appleの公式ドキュメントで GCD(Grand Central Dispatch) の使用を推奨されているのでそれを利用。

環境

  • MacOS High Sierra 10.13.6
  • Xcode 9.4.1

スレッドセーフではないサンプルプログラム

まずはスレッドセーフではないGateクラスとUserThreadクラスの実装です。

Gate.h
@interface Gate : NSObject

- (void) pass:(NSString*)name address:(NSString*)address;
- (NSString*) toString;

@end
Gate.m
@interface Gate () {
    NSInteger _counter;
    NSString *_name;
    NSString *_address;
}

@end

@implementation Gate

- (id) init {
    if(self = [super init]) {
        _counter = 0;
        _name = @"Nobody";
        _address = @"nowhere";
    }
    return self;
}

- (void) pass:(NSString*)name address:(NSString*)address {
    _counter++;
    _name = name;
    _address = address;
    [self check];
}

- (NSString*) toString {
    return [NSString stringWithFormat:@"No. %@ %@, %@", @(_counter), _name, _address];
}

- (void) check {
    if(![[_name substringToIndex:1] isEqualToString:[_address substringToIndex:1]]) {
        NSLog(@"**** BROKEN **** %@", [self toString]);
    }
}

@end
UserThread.h
@interface UserThread : NSObject

- (id) initWithGate:(Gate*)gate name:(NSString*)myName address:(NSString*)myAddress;
- (void) start;

@end
UserThread.m
@interface UserThread () {
    Gate *_gate;
    NSString *_myName;
    NSString *_myAddress;
}

@end

@implementation UserThread

- (id) initWithGate:(Gate*)gate name:(NSString*)myName address:(NSString*)myAddress {
    if(self = [super init]) {
        _gate = gate;
        _myName = myName;
        _myAddress = myAddress;
    }
    return self;
}

- (void) start {
    dispatch_queue_t queue = dispatch_queue_create("co.jp.SingleThreadedExecution.Queue", NULL);
    dispatch_async(queue, ^{
        [self run];
    });
}

- (void) run {
    NSLog(@"%@ BEGIN",_myName);
    while (true) {
        [_gate pass:_myName address:_myAddress];
    }
}

@end
ViewController.m
- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [self init];
}

- (void)init {
    Gate *gate = [[Gate alloc] init];

    [[[UserThread alloc] initWithGate:gate name:@"Alice" address:@"Alaska"] start];
    [[[UserThread alloc] initWithGate:gate name:@"Bobby" address:@"Brazil"] start];
    [[[UserThread alloc] initWithGate:gate name:@"Chris" address:@"Canada"] start];
}

@end

実行結果

実行してみると、ログにBROKENが出力されます。

Alice BEGIN
Bobby BEGIN
Chris BEGIN
**** BROKEN **** No. 4947 Alice, Alaska
**** BROKEN **** No. 5569 Alice, Alaska
**** BROKEN **** No. 6149 Bobby, Brazil
**** BROKEN **** No. 13204 Chris, Canada

スレッドセーフに修正

Gateクラスを @synchronized を使用してスレッドセーフに修正。

Gate.m
- (void) pass:(NSString*)name address:(NSString*)address {
    @synchronized(self) {
        _counter++;
        _name = name;
        _address = address;
        [self check];
    }
}

- (NSString*) toString {
    NSString *ret;
    @synchronized(self) {
        ret = [NSString stringWithFormat:@"No. %@ %@, %@", @(_counter), _name, _address];
    }
    return ret;
}

実行結果

実行してみると、ログにBROKENが出力されなくなりました。

Alice BEGIN
Bobby BEGIN
Chris BEGIN

サンプルソース

その他

今回 @synchronized を使用しましたが、「スレッドプログラミングガイド」にあるとおりNSLockクラスやNSRecursiveLockクラスを使用してみるのもいいと思います。

参考元

4
2
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
4
2