Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
AsyncStreamでマルチブロードキャストを実装する
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
1mash0
May 20, 2026
Technology
260
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
AsyncStreamでマルチブロードキャストを実装する
DMM.swift #5
1mash0
May 20, 2026
More Decks by 1mash0
See All by 1mash0
SwiftData: Dive into inheritance and schema migration - Swift愛好会スピンオフ WWDC25セッション要約会
1mash0
0
470
SwiftDataのカスタムデータストアを試してみた
1mash0
0
330
Other Decks in Technology
See All in Technology
しぶいSRE: サーバから見えない障害にどう向き合うか。ラストワンマイルのデバッグ実践 / Shibui SRE
kanny
12
5.5k
AIDLC_ヤフーショッピングの取り組み
lycorptech_jp
PRO
0
580
プライバシー保護の理論と実践
lycorptech_jp
PRO
1
250
SREとQA 二人三脚で進めるSLO運用/sre-qa-slo
sugitak
0
110
「ちゃんとやっている」は独りよがりだった ― 不安に寄り添うインシデント対応へ / Towards incident response that addresses anxieties
chmikata
1
4.3k
Oracle Exadata Database Service on Cloud@Customer X11M (ExaDB-C@C) サービス概要
oracle4engineer
PRO
2
8.4k
知らん間に、回ってる
ming_ayami
0
370
ADDF - ループエンジニアリングするフレームワークを作ったら/I Didn't Set Out to Build Loop Engineering, But ADDF Did
fruitriin
0
100
最適な自走を最小限の支援で — M&Aで拡大する組織で少人数SREが挑んだ1年 / SRE NEXT 2026
genda
0
710
クラウド上のデータ復旧で見落としがちな制約: 医療系 SaaS の BCP 設計から得た教訓
kakehashi
PRO
0
3k
Amazon EVS で VCF 9.0 / 9.1 のサポート開始まとめ
mtoyoda
0
280
貴方はどのエンジニアリングを磨くのか
hatyibei
0
110
Featured
See All Featured
How STYLIGHT went responsive
nonsquared
100
6.2k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
250
Navigating Team Friction
lara
192
16k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
310
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.7k
Un-Boring Meetings
codingconduct
0
340
Fashionably flexible responsive web design (full day workshop)
malarkey
408
66k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.4k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
270
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.9k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Transcript
͍·͑͢ "TZOD4USFBNͰϚϧνϒϩʔυ ΩϟετΛ࣮͢Δ %..TXJGU !NBTO@
"TZOD4USFBN w "TZOD4USFBN"TZOD4FRVFODFʹ४ڌ w ඇಉظΠςϨʔλΛखಈͰ࣮͢Δ͜ͱͳ͘ඇಉظγʔέϯεΛ࡞Ͱ͖Δ w DBMMCBDLEFMFHBUFϕʔεͷ"1*ΛBTZODBXBJUʹରԠͤ͞Δͷʹదͯ͠Δ w "TZOD4USFBN$POUJOVBUJPOΛड͚औΔΫϩʔδϟΛ༻ͯ͠ॳظԽ͢Δ w
$POUJOVBUJPOZJFME @ ϝιουΛݺͼग़ͯ͠ཁૉΛετϦʔϜʹ͢ w $POUJOVBUJPO fi OJTI Ͱऴྃ͢Δ
let stream = AsyncStream<Int> { continuation in Task { var
count = 1 while !Task.isCancelled { continuation.yield(count) count += 1 try? await Task.sleep(for: .seconds(1)) } } } let taskA = Task { for await value in stream { print("Task A:", value) } } let taskB = Task { for await value in stream { print("Task B:", value) } } try? await Task.sleep(for: .seconds(3)) taskA.cancel() try? await Task.sleep(for: .seconds(3))
"TZOD4USFBNϒϩʔυΩϟετͰͳ͍ w ྫ͑$PNCJOFͷ1BTTUISPVHI4VCKFDUʮϒϩʔυΩϟετ͢Δʯͱઆ໌ ͞Ε͍ͯΔ ʮA subject that broadcasts elements to
downstream subscribers.ʯ w Ұํ"TZOD4USFBN"TZOD4FRVFODFΛ࡞͢Δܕͱͯ͠આ໌͞Ε͍ͯΔ ʮAn asynchronous sequence generated from a closure that calls a continuation to produce new elements.ʯ
4&
"TZOD4USFBNͰϚϧνϒϩʔυΩϟετͷ࣮ํ๏ w ߪಡऀ͝ͱʹ"TZOD4USFBNΛੜ͢Δ w ߪಡऀ͝ͱͷ$POUJOVBUJPOΛ66*%Ͱཧ͢Δ w PO5FSNJOBUJPOͰऴྃͨ͠$POUJOVBUJPOΛআ͢Δ w ݩͷΠϕϯτιʔε͚ͭͩʹ͢Δ w
શ$POUJOVBUJPOZJFME @ ͢Δ
"TZOD4USFBNͰϚϧνϒϩʔυΩϟετͷ࣮ํ๏ w ߪಡऀ͝ͱʹ"TZOD4USFBNΛੜ͢Δ w ߪಡऀ͝ͱͷ$POUJOVBUJPOΛ66*%Ͱཧ͢Δ w PO5FSNJOBUJPOͰऴྃͨ͠$POUJOVBUJPOΛআ͢Δ w ݩͷΠϕϯτιʔε͚ͭͩʹ͢Δ w
શ$POUJOVBUJPOZJFME @ ͢Δ
private var continuations: [UUID: AsyncStream<Int>.Continuation] = [:] func stream() ->
AsyncStream<Int> { let id = UUID() let (stream, continuation) = AsyncStream<Int>.makeStream( of: Int.self, bufferingPolicy: self.bufferingPolicy ) continuations[id] = continuation continuation.onTermination = { [weak self] _ in Task { await self?.removeContinuation(id) } } return stream }
4&
"TZOD4USFBNͰϚϧνϒϩʔυΩϟετͷ࣮ํ๏ w ߪಡऀ͝ͱʹ"TZOD4USFBNΛੜ͢Δ w ߪಡऀ͝ͱͷ$POUJOVBUJPOΛ66*%Ͱཧ͢Δ w PO5FSNJOBUJPOͰऴྃͨ͠$POUJOVBUJPOΛআ͢Δ w ݩͷΠϕϯτιʔε͚ͭͩʹ͢Δ w
શ$POUJOVBUJPOZJFME @ ͢Δ
continuation.onTermination = { [weak self] _ in Task { await
self?.removeContinuation(id) } } private func removeContinuation(_ id: UUID) { continuations[id] = nil }
"TZOD4USFBNͰϚϧνϒϩʔυΩϟετͷ࣮ํ๏ w ߪಡऀ͝ͱʹ"TZOD4USFBNΛੜ͢Δ w ߪಡऀ͝ͱͷ$POUJOVBUJPOΛ66*%Ͱཧ͢Δ w PO5FSNJOBUJPOͰऴྃͨ͠$POUJOVBUJPOΛআ͢Δ w ݩͷΠϕϯτιʔε͚ͭͩʹ͢Δ w
શ$POUJOVBUJPOZJFME @ ͢Δ
private var task: Task<Void, Never>? func start() { task?.cancel() task
= Task { [weak self] in var count = 1 while !Task.isCancelled { await self?.yield(count) count += 1 try? await Task.sleep(for: .seconds(1)) } } }
"TZOD4USFBNͰϚϧνϒϩʔυΩϟετͷ࣮ํ๏ w ߪಡऀ͝ͱʹ"TZOD4USFBNΛੜ͢Δ w ߪಡऀ͝ͱͷ$POUJOVBUJPOΛ66*%Ͱཧ͢Δ w PO5FSNJOBUJPOͰऴྃͨ͠$POUJOVBUJPOΛআ͢Δ w ݩͷΠϕϯτιʔε͚ͭͩʹ͢Δ w
શ$POUJOVBUJPOZJFME @ ͢Δ
func yield(_ value: Int) { for continuation in continuations.values {
continuation.yield(value) } } func finish() { task?.cancel() task = nil for continuation in continuations.values { continuation.finish() } continuations.removeAll() }
actor Broadcast { typealias BufferingPolicy = AsyncStream<Int>.Continuation .BufferingPolicy private var
continuations: [UUID: AsyncStream<Int>.Continuation] = [:] private let bufferingPolicy: BufferingPolicy private var task: Task<Void, Never>? init(bufferingPolicy: BufferingPolicy) { self.bufferingPolicy = bufferingPolicy } func stream() -> AsyncStream<Int> { let id = UUID() let (stream, continuation) = AsyncStream<Int>.makeStream( of: Int.self, bufferingPolicy: self.bufferingPolicy ) continuations[id] = continuation continuation.onTermination = { [weak self] _ in Task { await self?.removeContinuation(id) } } return stream } func start() { task?.cancel() task = Task { [weak self] in var count = 1 while !Task.isCancelled { await self?.yield(count) count += 1 try? await Task.sleep(for: .seconds(1)) } } } func yield(_ value: Int) { for continuation in continuations.values { continuation.yield(value) } } func finish() { task?.cancel() task = nil for continuation in continuations.values { continuation.finish() } continuations.removeAll() } private func removeContinuation(_ id: UUID) { continuations[id] = nil } }
let broadcast = Broadcast(bufferingPolicy: .bufferingNewest(1)) let stream1 = await broadcast.stream()
let stream2 = await broadcast.stream() let taskA = Task { for await value in stream1 { print("Task A:", value) } } let taskB = Task { for await value in stream2 { print("Task B:", value) } } await broadcast.start() try? await Task.sleep(for: .seconds(3)) taskA.cancel() try? await Task.sleep(for: .seconds(3))
·ͱΊ w ߪಡऀ͝ͱʹ"TZOD4USFBNΛੜ͢Δ w ߪಡऀ͝ͱͷ"TZOD4USFBN$POUJOVBUJPOΛཧΛ͢Δ w J04ະຬͳΒ66*%Ͱཧ w J04Ҏ্ͳΒ4FUͰཧ w
ݩͷΠϕϯτιʔε͕ॏෳ͠ͳ͍Α͏ʹҙ
TXJGUBTZODBMHPSJUINT w όʔδϣϯ ͔Β4IBSFػೳ͕ಋೖ͞ΕͯϚϧνϒϩʔυΩϟ ετʹରԠͨ͠ w ߪಡͯ͠Δ5BTLΛΩϟϯηϧͯ͠ɺͷ"TZOD4USFBNʹΩϟϯηϧ͕ ͠ͳ͍ͨΊҙ w
ߪಡͯ͠Δ5BTL͕ͬͯΔͱDPOUJOVBUJPOPO5FSNJOBUJPOʹྲྀΕͯ͜ͳ͍
import AsyncAlgorithms let stream = AsyncStream<Int> { continuation in Task
{ var count = 1 while !Task.isCancelled { continuation.yield(count) count += 1 try? await Task.sleep(for: .seconds(1)) } } }.share() let taskA = Task { for await value in stream { print("Task A:", value) } } let taskB = Task { for await value in stream { print("Task B:", value) } } try? await Task.sleep(for: .seconds(3)) taskA.cancel() try? await Task.sleep(for: .seconds(3))
·ͱΊ w ߪಡऀ͝ͱʹ"TZOD4USFBNΛੜ͢Δ w ߪಡऀ͝ͱͷ"TZOD4USFBN$POUJOVBUJPOΛཧΛ͢Δ w J04ະຬͳΒ66*%Ͱཧ w J04Ҏ্ͳΒ4FUͰཧ w
ݩͷΠϕϯτιʔε͕ॏෳ͠ͳ͍Α͏ʹҙ w TXJGUBTZODBMHPSJUINTΛ͓͏