Slide 1

Slide 1 text

KotlinConf 2018 ワークショップに参加してきた あんざいゆき(@yanzm)

Slide 2

Slide 2 text

Workshop day • 10⽉3⽇(KotlinConf day1) • カンファレンスとは別料⾦ : €649 • 9:00 〜 17:00 • 朝ごはん・昼ごはんあり • 会場はカンファレンスと同じ • https://kotlinconf.com/workshops/

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Workshops • Up and running with Kotlin by Svetlana Isakova • Asynchronous Programming with Kotlin by Roman Elizarov • Developing Android Apps in Kotlin by Florina Muntenescu and Sean McQuillan • Building a Full Stack Web Application in Kotlin by Garth Gilmour • Refactoring to Kotlin by Duncan McGregor and Nat Pryce

Slide 9

Slide 9 text

Asynchronous Programming with Kotlin • 講師 : Roman Elizarov • https://github.com/elizarov • 内容 : coroutines

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

事前準備 • 最新の IntelliJ IDEA (CE 可) をインストールしておく • Kotlin plugin の最新の EAP 1.3 version をインストールしておく • [Tools] → [Kotlin] → [Configure Kotlin Plugin Updates] → Early Access Preview 1.3 → Check & Install the latest version • Github ログイン⽤のトークン • [Settings] →[Developer Setting] → Personal Access Tokens → Generate New Token

Slide 14

Slide 14 text

Outline • Asynchronous programming, Introduction to Coroutines • Coroutines vs Threads, Context and Cancellation • Coroutines and Concurrency • CSP with Channels and Actors

Slide 15

Slide 15 text

サンプルプロジェクト • project/ContributorsUI.main() • デスクトップアプリ(Swing利⽤) • GitHub から kotlin Organization のリポジトリ⼀覧を取 得し、各リポジトリの contributors を取得し集計する • part1async/ 〜 part4csp/ • ⼩さい demo コード集

Slide 16

Slide 16 text

• Kotlin Organization のリポジトリを取得 • List • リポジトリごと Contributors を取得 • List • ユーザーの重複をflattenして集計 Repo1 - User11 - User12 - … Repo2 - User21 - User22 - … …

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

enum class Variant { BLOCKING, // Request1Blocking BACKGROUND, // Request2Background CALLBACKS, // Request3Callbacks COROUTINE, // Request4Coroutine PROGRESS, // Request5Progress CANCELLABLE, // Request5Progress (too) CONCURRENT, // Request6Concurrent GATHER, // Request8Gather ACTOR // Request9Actor }

Slide 19

Slide 19 text

Programming styles • 1. Blocking threads • 2. Callbacks • 3. Futures • 4. Kotlin coroutines

Slide 20

Slide 20 text

Programming styles • 1. Blocking threads • 2. Callbacks • 3. Futures • 4. Kotlin coroutines COROUTINE, // Request4Coroutine PROGRESS, // Request5Progress CANCELLABLE, // Request5Progress (t CONCURRENT, // Request6Concurrent GATHER, // Request8Gather ACTOR // Request9Actor FUTURE, // Request7Future CALLBACKS, // Request3Callbacks BACKGROUND, // Request2Background

Slide 21

Slide 21 text

ウォーミングアップ data class User( val login: String, val contributions: Int ) fun List.aggregate(): List = TODO List の中から重複した login の User を1つにまとめる まとめるとき contributions を⾜し合わせる contributions の多い順にソートする

Slide 22

Slide 22 text

ウォーミングアップ fun List.aggregate(): List = groupingBy { it.login } .reduce { login, a, b -> User(login, a.contributions + b.contributions) } .values .sortedByDescending { it.contributions } data class User( val login: String, val contributions: Int ) List の中から重複した login の User を1つにまとめる まとめるとき contributions を⾜し合わせる contributions の多い順にソートする

Slide 23

Slide 23 text

Background fun loadContributorsBackground(req: RequestData, callback: (List) -> Unit) { thread { val users = loadContributorsBlocking(req) callback(users) } } スレッドで

Slide 24

Slide 24 text

Callbacks fun loadContributorsCallbacks(req: RequestData, callback: (List) -> Unit) { val service = createGitHubService(req.username, req.password) service.listOrgRepos(req.org).responseCallback { repos -> … } } inline fun Call.responseCallback(crossinline callback: (T) -> Unit) { enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { checkResponse(response) callback(response.body()!!) } override fun onFailure(call: Call, t: Throwable) { log.error("Call failed", t) } }) } Retrofit の enqueue を利⽤

Slide 25

Slide 25 text

Coroutines launch { val users = loadContributors(req) updateResults(users) } suspend fun loadContributors(req: RequestData) : List { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val contribs = repos.flatMap { repo -> val users = service.listRepoContributors(req.org, repo.name).await() users }.aggregate() return contribs } loadContributors() 部分は blocking と同じ

Slide 26

Slide 26 text

Progress suspend fun loadContributorsProgress(req: RequestData, callback: (List) -> Unit) { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() var contribs = listOf() for (repo in repos) { val users = service.listRepoContributors(req.org, repo.name).await() contribs = (contribs + users).aggregateSlow() callback(contribs) } } Coroutines + Progress リポジトリロードごとに callback 呼び出し

Slide 27

Slide 27 text

Cancellable private fun Job.updateCancelJob() { updateEnabled(false) val listener = ActionListener { cancel() } cancel.addActionListener(listener) launch { join() updateEnabled(true) cancel.removeActionListener(listener) } } キャンセルボタンが押されたら Job を cancel() launch { loadContributorsProgress(req) { users -> updateResults(users) } }.updateCancelJob()

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Concurrent suspend fun loadContributorsConcurrent(req: RequestData): List = coroutineScope { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val contribs = repos.map { repo -> async { val users = service.listRepoContributors(req.org, repo.name).await() users } }.awaitAll().flatten().aggregate() contribs } async でリポジトリごとの Contributors ロードを並列化

Slide 31

Slide 31 text

Future val future = loadContributorsConcurrentAsync(req) updateCancelFuture(future) future.thenAccept { users -> SwingUtilities.invokeLater { updateResults(users) } } fun loadContributorsConcurrentAsync( req: RequestData ): CompletableFuture> = GlobalScope.future { loadContributorsConcurrent(req) }

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Gather suspend fun loadContributorsGather( req: RequestData, callback: suspend (List) -> Unit ) = coroutineScope { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val channel = Channel>() for (repo in repos) { launch { val users = service.listRepoContributors(req.org, repo.name).await() channel.send(users) } } var contribs = emptyList() repeat(repos.size) { val users = channel.receive() contribs = (contribs + users).aggregateSlow() callback(contribs) } } Channel を使⽤

Slide 35

Slide 35 text

Actor suspend fun loadContributorsActor( req: RequestData, uiUpdateActor: SendChannel> ) = coroutineScope { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val aggregator = aggregatorActor(uiUpdateActor) val requests = Channel() val workers = List(4) { workerJob(requests, aggregator) } for (repo in repos) { requests.send(WorkerRequest(service, req.org, repo.name)) } requests.close() workers.joinAll() aggregator.close() } List → requests : Channel → aggregator: SendChannel> → uiUpdateActor : SendChannel> 4並列でWorkRequestを処理

Slide 36

Slide 36 text

fun CoroutineScope.aggregatorActor( uiUpdateActor: SendChannel> ) = actor> { var contribs: List = emptyList() // STATE for (users in channel) { contribs = (contribs + users).aggregateSlow() uiUpdateActor.send(contribs) } } class WorkerRequest( val service: GitHubService, val org: String, val repo: String ) fun CoroutineScope.workerJob( requests: ReceiveChannel, aggregator: SendChannel> ) = launch { for (req in requests) { val users = req.service.listRepoContributors(req.org, req.repo).await() log.info("${req.repo}: loaded ${users.size} contributors") aggregator.send(users) } } actor { } を使⽤

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Have a Nice Kotlin! • blog : Y.A.M の雑記帳 • y-anz-m.blogspot.com • twitter : @yanzm (やんざむ) • uPhyca Inc. (株式会社ウフィカ)