새싹

DispatchGroup에 대해 알아보자 part 1.

Thor_yeom 2023. 8. 21. 00:13

 

개발을 하면서 DispatchQueue에 대해서 많이 들어보고 적용해보았을 것이다.

 

혹시 아직 못들어보셨거나 헷갈리시는 분들은 

DispatchQueue  해당 블로그를 참고하시면 됩니다.

 

이번 사례는 실제 프로젝트에 적용해보고 느낀점에 대해서 말해볼까 합니다. 

편한 말투로 진행하겠습니다.

 

DispatchGroup이 뭔가요?

핵심만 말하면

모든 작업이 끝날때까지 기다렸다가 마지막 작업을 실행함

 

실제 적용사례를 들어가기 전에

DispatchQueue를 간단히 정리 후 들어가겠습니다.

 

 

1. 일반적으로 코드를 실행하면 순서대로 진행 됩니다.

    func example1() {
        print("Start")
        
        for i in 1...100 {
            print(i, terminator: " ")
        }
        
        for i in 101...200 {
            print(i, terminator: " ")
        }
        for i in 201...300 {
            print(i, terminator: " ")
        }
        
        print("END")
    }
    
   /*
   Start
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 
296297 298 299 300 
END
*/

 

2. DispatchQueue.main.sync를 사용하면 어떻게 될까? 
데드락 현상이 발생합니다. 즉, 교착상태에 빠져 런타임 오류가 발생합니다.

   func example2() {
        print("Start")
        
        for i in 1...100 {
            print(i, terminator: " ")
        }
        
        DispatchQueue.main.sync {
            for i in 101...200 {
                print(i, terminator: " ")
            }
        }
        
        for i in 201...300 {
            print(i, terminator: " ")
        }
        
        print("END")
    }

데드락 상태

3. DispatchQueue.global().sync 으로 작성하면 어떻게 될까?

 

DispatchQueue.global().sync의 의미는

" 일단 Queue로 보내 그리고 다른 Thred에서 작업하는데 main에서 일이 끝나면 그때 실행해  "

즉, 다른 Thread로 보내지만 실상은 main에서 순서대로 하는 것과 똑같다.

    func example3() {
        print("Start")
        
        for i in 1...100 {
            print(i, terminator: " ")
        }
        
        DispatchQueue.global().sync {
            for i in 101...200 {
                print(i, terminator: " ")
            }
        }
        
        for i in 201...300 {
            print(i, terminator: " ")
        }
        
        print("END")
    }
    
   /*
   Start
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 62 63 64 65 66 67 68 69 70 71
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 
104 105 106 107 108 109 110 111 112 113 114 115 116 
117 118 119 120 121 122 123 124 125 126 127 128 129 
130 131 132 133 134 135 136 137 138 139 140 141 142
143 144 145 146 147 148 149 150 151 152 153 154 155 
156 157 158 159 160 161 162 163 164 165 166 167 168 
169 170 171 172 173 174 175 176 177 178 179 180 181 
182 183 184 185 186 187 188 189 190 191 192 193 194 
195 196 197 198 199 200 201 202 203 204 205 206 207 
208 209 210 211 212 213 214 215 216 217 218 219 220 221 
222 223 224 225 226 227 228 229 230 231 232 233 234 235
236 237 238 239 240 241 242 243 244 245 246 247 248 249
250 251 252 253 254 255 256 257 258 259 260 261 262 263 
264 265 266 267 268 269 270 271 272 273 274 275 276 277 
278 279 280 281 282 283 284 285 286 287 288 289 290 291
292 293 294 295 296 297 298 299 300
END

   */

 

본격적으로

async에 대해 들어가보자

 

4. DispatchQueue.main.async를 적용하면 어떻게 될까?

 

DispatchQueue.main.async의 의미는

" Queue로 보내고 main에서 일이 다 끝나면 Queue에 있는것을 main으로 보내  실행할게 " 

출력값을 보면 DispatchQueue.main.async로 되어 있는 부분이 가장 나중에 출력되는것을 

볼 수 있다. 

    func example4() {
        print("Start")
        
        for i in 1...50 {
            print(i, terminator: " ")
        }
        
        DispatchQueue.main.async {
            for i in 51...100 {
                print(i, terminator: " ")
            }
        }
        
        for i in 101...150 {
            print(i, terminator: " ")
        }
        
        print("END")
    }
    
    /*
    Start
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2
1 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 101 102 103 104 105 106
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 
137 138 139 140 141 142 143 144 145 146 147 148 149 150 END

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 
90 91 92 93 94 95 96 97 98 99 100 
    
    */

 

5. DispatchQueue.global().async를 적용하면 어떻게 될까?

 

DispatchQueue.global().async의 의미는

" Queue로 보내고 main에서는 다음일을 진행하고 Queue에서는 다른 Thread로 보내 비동기적으로 처리하자 "

출력되는 것을 보면 1 ~ 50 까지 출력이 되고, 그 다음부터 51 ~ 150이 비동기적으로 출력되는 것을 확인 할 수 있다. 

    func example5() {
        print("Start")
        
        for i in 1...50 {
            print(i, terminator: " ")
        }
        
        DispatchQueue.global().async {
            for i in 51...100 {
                print(i, terminator: " ")
            }
        }
        
        for i in 101...150 {
            print(i, terminator: " ")
        }
        
        print("END")
    }
    
    /*
    Start
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 101 51 52 53 
102 103 54 55 56 104 105 57 58 59 60 61 62 63 64 65
66 67 106 107 108 109 68 110 111 112 113 114 115 116 
117 118 119 120 121 122 123 124 125 126 127 128 129 
69 70 71 72 73 74 75 76 77 78 79 130 80 131 81 132 
133 134 135 136 137 138 139 140 141 142 143 144 145 
146 147 148 149 150 END
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 
    */

 

더 나아가서 

모든 for문에 DispatchQueue.global().async를 하면 어떻게 출력될까?

    func example5() {
        print("Start")
        DispatchQueue.global().async {
            for i in 1...50 {
                print(i, terminator: " ")
            }
        }
        
        DispatchQueue.global().async {
            for i in 51...100 {
                print(i, terminator: " ")
            }
        }
        DispatchQueue.global().async {
            for i in 101...150 {
                print(i, terminator: " ")
            }
        }
        
        print("END")
    }
    
    /*
Start
END
1 2 3 4 5 6 7 8 9 51 10 11 12 13 14 15 101 16
52 53 54 55 56 57 58 59 60 61 62 63 64 65 102
66 67 68 69 17 70 71 72 73 74 103 75 76 77 78 
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
18 95 104 19 20 21 22 105 106 107 108 109 110 111 
96 112 113 114 115 97 98 99 23 24 25 26 27 28 116 
117 118 119 120 121 122 123 124 125 100 126 29 127 
30 31 128 32 33 34 35 36 37 38 39 40 41 42 43 44 
45 46 47 48 49 129 130 50 131 132 133 134 135 136 
137 138 139 140 141 142 143 144 145 146 147 148
149 150 
    */

 

출력문보면 startEND가 먼저 출력이 됐고 for문이 돌아가면서
비동기적으로 출력되는 것을 확인 할 수 있다.

 

그렇다면! 

start부터 시작해서 END 순으로 출력되는 방법은 없을까?

이때 DispatchGroup()을 사용하면 된다.

 

DispatchGroup의 핵심

Group안에 묶어서 모든 작업이 끝나면 notify 끝나는것을 알린다.

 

예시를 보면 

    func example5() {
        print("Start")
        // group 생성
        let group = DispatchGroup()
        // group으로 묶기
        DispatchQueue.global().async(group: group) {
            for i in 1...50 {
                print(i, terminator: " ")
            }
        }
        // group으로 묶기
        DispatchQueue.global().async(group: group) {
            for i in 51...100 {
                print(i, terminator: " ")
            }
        }
        // group으로 묶기
        DispatchQueue.global().async(group: group) {
            for i in 101...150 {
                print(i, terminator: " ")
            }
        }
        // group에서 작업이 끝나면 notify로 알리고 클로저문 실행
        group.notify(queue: .main) {
            print("END")
        }
        
    }
    
    /*
Start
51 52 53 54 55 56 1 57 58 59 60 61 
62 63 64 65 66 67 68 69 70 71 72 73
74 101 102 103 104 105 106 107 75 76 
77 78 79 80 81 82 83 84 85 86 87 88 
89 90 91 92 93 94 95 96 97 98 99 100 
2 3 4 5 6 7 8 9 10 11 12 13 14 15 108
16 17 18 19 20 21 22 23 24 25 26 27 28 
29 30 31 32 33 34 109 110 111 35 112 113
114 115 116 117 36 37 38 39 118 119 120 
121 122 123 124 125 126 127 128 129 130 131
132 133 134 135 40 136 137 138 139 140 141 
142 41 143 144 42 43 44 45 46 47 48 49 50 145
146 147 148 149 150
END
    */

 

실행을 해보면 for문의 출력문은 바뀔지 몰라도

start에서 END 순으로 출력되는 것을 확인 할 수 있다.

 

 

다음편에는 DispatchQueue.global().async()로 구성된

for문의 출력문을 순서대로 출력하는 방법을 알아보겠습니다