協程作用域

Scope獨立作用域

Scope獨立作用域與沒有獨立作用域

特性 沒有 Scope (launch{}) 有 Scope (scope.launch{})
父子關係 與 runBlocking 是父子 與 runBlocking 無父子關係
join 自動join() 手動join()
調度器 繼承父協程 使用自定義調度器

join

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 沒有 Scope - 自動join
fun example1() = runBlocking {
    launch {
        delay(1000)
        println("一定會執行") // runBlocking 會等待
    }
    // 自動等待所有子協程
}

// 有 Scope - 自己寫join()  
fun example2() = runBlocking {
    val scope = CoroutineScope(Dispatchers.Default)
    scope.launch {
        delay(1000)
        println("可能不會執行!") // 如果 runBlocking 先結束
    }
    // runBlocking 結束時不會等待 scope 中的協程
}

CoroutineScope

建立一個獨立CoroutineScope作用域,runBlocking的作用域為BlockingCoroutine。

CoroutineScope下面有一個子協程,名字為job1。

img

調度器設為預設Dispatchers.Default。

使用scope.launch{},建立子協程並啟動子協程。

1
2
3
4
5
6
7
8
@Test
fun coroutin07() = runBlocking {
  val scope = CoroutineScope(Dispatchers.Default)
  val job1 = scope.launch {
    delay(1000)
    println("job1")
  }
}

但執行完卻沒有任何結果,這是為什麼?
runBlocking協程與作用域協程,沒有父子關係,所以runBlocking協程執行完畢就結束,不會等待CoroutineScope下的job1子協程執行完。

delay暫停

增加一個delay(大於1000),因為CoroutineScope下的子協程執行時為1秒,要讓runBlocking暫停超過1秒,再結束runBlocking協程。

delay()參數要大於1000,等到scope執行完畢,runBlocking協程才能結束。

1
2
3
4
5
6
7
8
  fun coroutin07() = runBlocking {
    val scope = CoroutineScope(Dispatchers.Default)
    scope.launch {
      delay(1000)
      println("job1")
    }
    delay(2000)
  }
job1

join 等待

runBlocking協程「等待」scope.job完成,runBlocking才能結束。

1
2
3
4
5
6
7
8
fun coroutin07() = runBlocking {
  val scope = CoroutineScope(Dispatchers.Default)
  val job = scope.launch {
    delay(1000)
    println("job1")
  }
  job.join()
}
job1

GlobalScope

GlobalScope也是獨立作用域,加上join,runBlocking才會等待GlobalScope執行完畢。

1
2
3
4
5
6
7
8
  @Test
  fun coroutin08() = runBlocking {
    val job = GlobalScope.launch {
      delay(1000)
      println("job1")
    }
    job.join()
  }
job1

Scope使用時機

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
// 場景 1: 需要自定義調度器
fun processInBackground() {
    val ioScope = CoroutineScope(Dispatchers.IO)
    ioScope.launch {
        // 在IO線程執行網路請求
    }
}

// 場景 2: 長時間運行的後台任務
class MyService {
    private val serviceScope = CoroutineScope(Dispatchers.Default)
    
    fun start() {
        serviceScope.launch {
            while (isActive) {
                // 長時間運行的任務
                delay(5000)
            }
        }
    }
    
    fun stop() {
        serviceScope.cancel() // 需要手動取消
    }
}

// 場景 3: 在非協程環境中啟動協程
fun nonCoroutineFunction() {
    val scope = CoroutineScope(Dispatchers.Main)
    scope.launch {
        // 在Android主線程更新UI
    }
}

coroutineScope runBlocking

coroutineScope雖然名字有Scope,但開頭字母為小寫,不是獨立作用域,父親是runBlocking,runBlocking是BlockingCoroutine的作用域。

img

2個suspend函式:

1
2
3
4
5
6
7
8
9
10
  private suspend fun doOne():Int {
    // 1秒
    delay(1000)
    return 10
  }
  private suspend fun doTwo():Int {
    // 2秒
    delay(1000)
    return 20
  }

coroutineScope 非阻塞協程

以下程式碼,二個suspend函式同時執行。
coroutineScope會等待子協程(suspend 函式),執行完畢。

1
2
3
4
5
6
7
8
9
  fun coroutin05() = runBlocking {
    val startTime = System.currentTimeMillis()
    coroutineScope {
        val one = doOne()
        val two = doTwo()
    }
    val duration = System.currentTimeMillis() - startTime
    println(" in ${duration} ms")
  }
 in 2 ms

runBlocking 阻塞協程

以下程式碼,二個suspend函式逐一執行,doOne()執行完,才輪到doTwo執行()。
所以共執行約2秒鐘左右。
runBlocking會等待子協程(suspend 函式),執行完畢。

1
2
3
4
5
6
7
8
9
  fun coroutin05() = runBlocking {
    val startTime = System.currentTimeMillis()
    runBlocking {
        val one = doOne()
        val two = doTwo()
    }
    val duration = System.currentTimeMillis() - startTime
    println(" in ${duration} ms")
  }
 in 2009 ms

results matching ""

    No results matching ""