학습
Scala 동시성 프로그래밍 기초
Scala 동시성 프로그래밍 기초 — 중급
동시성 프로그래밍 기초
1. 도입 (왜 배워야 하는가)
동시성 프로그래밍은 현대 소프트웨어 개발에서 필수적인 기술 중 하나입니다. 특히, 대규모 분산 시스템이나 멀티스레드 애플리케이션을 개발할 때 동시성을 효과적으로 활용해야 합니다. 예를 들어, 쇼핑몰 웹사이트에서 사용자가 상품을 장바구니에 추가할 때, 여러 사용자가 동시에 요청을 보낼 수 있습니다. 이때, 동시성 프로그래밍을 통해 각각의 요청을 효율적으로 처리하지 않으면 데이터 충돌이나 응답 지연이 발생할 수 있습니다.
동시성 프로그래밍을 모르면 다음과 같은 문제가 발생할 수 있습니다:
- 데이터 무결성 문제: 여러 스레드가 동시에 데이터에 접근하여 수정할 경우, 예기치 않은 결과가 발생할 수 있습니다.
- 성능 저하: 동시성을 활용하지 않으면 자원 낭비가 발생하고, 시스템 성능이 저하될 수 있습니다.
2. 핵심 개념 설명
동시성 프로그래밍에서 이해해야 할 몇 가지 핵심 개념은 다음과 같습니다.
| 개념 | 정의 |
|---|---|
| 스레드 | 프로그램의 실행 단위로, 여러 스레드가 동시에 실행될 수 있음. |
| 동기화 | 여러 스레드가 공유 자원에 접근할 때 발생할 수 있는 문제를 해결하기 위한 기술. |
| 경량 스레드 | 더 적은 자원으로 스레드를 생성할 수 있는 기능. Scala의 Future와 같은 기능이 이에 해당. |
비유하자면, 스레드는 여러 요리사가 동시에 주방에서 요리를 하는 것과 같습니다. 그런데 만약 여러 요리사가 같은 냄비를 사용하려고 할 때, 누가 먼저 사용할지를 정해야 합니다. 이때 '동기화' 기술이 필요합니다.
3. 기본 코드 예제
아래는 Scala에서 스레드를 생성하는 가장 기본적인 예제입니다.
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val futureTask = Future {
// 비동기 작업
Thread.sleep(1000) // 1초 대기
"작업 완료"
}
// 결과를 기다림
val result = Await.result(futureTask, 2.seconds)
println(result) // 출력: 작업 완료
실행 결과:
작업 완료
코드 설명:
import scala.concurrent.{Future, Await}: Future와 Await를 사용하기 위해 필요한 라이브러리를 가져옵니다.val futureTask = Future { ... }: Future를 사용하여 비동기 작업을 정의합니다.Thread.sleep(1000): 1초 동안 실행을 멈춥니다. (여기서는 비동기 작업의 예시로 사용)Await.result(futureTask, 2.seconds):futureTask의 결과를 기다립니다. 최대 2초까지 기다립니다.println(result): 비동기 작업의 결과를 출력합니다.- 잘못된 코드
- 올바른 코드
- 잘못된 코드
- 올바른 코드
- 동시성 프로그래밍은 대규모 시스템에서 데이터 무결성을 유지하는 데 필수적입니다.
- 스레드와 동기화 개념을 이해해야 합니다.
- Scala에서
Future를 사용하면 비동기 작업을 쉽게 처리할 수 있습니다. - 코드 작성 시 동기화와 결과 처리를 신경 써야 합니다.
- Akka로 배우는 분산 시스템
- Scala의 비동기 프로그래밍 패턴
- Reactive Programming에 대한 이해
4. 실전 활용 예제
예제 1: 쇼핑몰 장바구니 구현
쇼핑몰에서 사용자가 상품을 장바구니에 추가할 때, 여러 사용자가 동시에 요청할 수 있습니다. 이때, 각 요청이 독립적으로 처리되도록 동시성을 활용할 수 있습니다.
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.collection.mutable
object ShoppingCart {
val cart: mutable.ListBuffer[String] = mutable.ListBuffer()
def addItem(item: String): Unit = {
Future {
// 동시성 문제를 피하기 위한 동기화
this.synchronized {
cart += item
}
println(s"$item 추가됨")
}
}
}
// 사용 예
ShoppingCart.addItem("상품 A")
ShoppingCart.addItem("상품 B")
// 결과를 기다림
Thread.sleep(1000) // 모든 작업이 완료될 때까지 대기
println(ShoppingCart.cart) // 출력: ListBuffer(상품 A, 상품 B)
예제 2: 데이터 처리
대량의 데이터를 비동기적으로 처리할 때 동시성을 활용할 수 있습니다.
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
val dataList = List(1, 2, 3, 4, 5)
val processedData = dataList.map { data =>
Future {
// 데이터 처리 (여기선 간단히 2배로 만듦)
Thread.sleep(500) // 0.5초 대기
data * 2
}
}
// 모든 Future의 결과를 기다림
val result = Future.sequence(processedData)
Await.result(result, 10.seconds).foreach(println) // 출력: 2, 4, 6, 8, 10
5. 흔한 실수와 주의사항
실수 1: 동기화 문제
// 동기화 없이 공유 자원에 접근
def addItem(item: String): Unit = {
cart += item
}
def addItem(item: String): Unit = {
this.synchronized {
cart += item
}
}
실수 2: Future 결과 처리 누락
val futureTask = Future {
Thread.sleep(1000)
"작업 완료"
}
// 결과를 기다리지 않음
val result = Await.result(futureTask, 2.seconds)
println(result)
6. 정리
다음에 학습하면 좋은 주제: