de[v|b]log

ShellScript, Coffee, iOS/OSX Dev
Origin: Himajinworks.
About.

よく GCDdispatch_semaphore_create の引数が 0 ってどういう意味だっけとなるので確認した。
どこでこれを知りたくなるかというと、(あまりやりたくないが)非同期処理を同期処理として扱いたいというケース。

[Objective-C] UIAlertViewを同期処理する - Qiita の記事の例がよい一例となると思う。

(尚、semaphoreなので dispatch_semaphore_wait を関数の開始直後においてブロッキングしたほうが良いというのは承知している。)

例となるコードの実行

実行対象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
 *  This class shows thread run example
 */
@interface ThreadTask : NSObject

@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@property (nonatomic, strong) dispatch_queue_t queue;

- (void)invokeThreadBlockingTaskWithSemaphoreValue:(long)semaphoreValue;

@end

@implementation ThreadTask

/**
 *  Initialize the state and run thread blocking task for 5 times
 */
- (void)invokeThreadBlockingTaskWithSemaphoreValue:(long)semaphoreValue
{
  self.semaphore = dispatch_semaphore_create(semaphoreValue);
  self.queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  
  for (long i = 0; i < 5; i++) {
    [self blockingExampleWithTaskId:i];
  }
}

/**
 *  Run thread blocking task
 */
- (void)blockingExampleWithTaskId:(long)taskId
{
  NSLog(@"+ [%02ld: start]", taskId);
  
  dispatch_async(self.queue, ^{
    [ThreadTask heavyTaskWithTaskId:taskId];
    
    dispatch_semaphore_signal(self.semaphore);
  });
  
  while (dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_NOW)) {
    NSLog(@"| [%02ld: wait ]", taskId);
    [NSThread sleepForTimeInterval:0.8];
  }
  
  NSLog(@"+ [%02ld: end  ]", taskId);
}

/**
 *  Example of heavy task
 */
+ (void)heavyTaskWithTaskId:(long)taskId
{
  NSLog(@"├ [%02ld: start] heavy task", taskId);
  
  [NSThread sleepForTimeInterval:1];
  
  NSLog(@"├ [%02ld: end  ] heavy task", taskId);
}

@end

これを以下のように実行する。

1
2
3
4
5
6
7
NSLog(@"<TopLevel> Run thread example with semaphore value = 0");
[[[ThreadTask alloc] init] invokeThreadBlockingTaskWithSemaphoreValue:0];
  
printf("\n");
  
NSLog(@"<TopLevel> Run thread example with semaphore value = 1");
[[[ThreadTask alloc] init] invokeThreadBlockingTaskWithSemaphoreValue:1];

結果

<TopLevel> Run thread example with semaphore value = 0
+ [00: start]
| [00: wait ]
├ [00: start] heavy task
| [00: wait ]
├ [00: end  ] heavy task
+ [00: end  ]
+ [01: start]
| [01: wait ]
├ [01: start] heavy task
| [01: wait ]
├ [01: end  ] heavy task
+ [01: end  ]
+ [02: start]
| [02: wait ]
├ [02: start] heavy task
| [02: wait ]
├ [02: end  ] heavy task
+ [02: end  ]
+ [03: start]
| [03: wait ]
├ [03: start] heavy task
| [03: wait ]
├ [03: end  ] heavy task
+ [03: end  ]
+ [04: start]
| [04: wait ]
├ [04: start] heavy task
| [04: wait ]
├ [04: end  ] heavy task
+ [04: end  ]

<TopLevel> Run thread example with semaphore value = 1
+ [00: start]
+ [00: end  ]
├ [00: start] heavy task
+ [01: start]
| [01: wait ]
├ [01: start] heavy task
| [01: wait ]
├ [01: end  ] heavy task
├ [00: end  ] heavy task
+ [01: end  ]
+ [02: start]
+ [02: end  ]
├ [02: start] heavy task
+ [03: start]
| [03: wait ]
├ [03: start] heavy task
| [03: wait ]
├ [02: end  ] heavy task
├ [03: end  ] heavy task
+ [03: end  ]
+ [04: start]
+ [04: end  ]
├ [04: start] heavy task
├ [04: end  ] heavy task

結論

value=0, value=1 の実行ログを見ての通りだが、 dispatch_semaphore_create(n) のとき、 n + 1 が同時実行可能数。

参考