天天看點

Dart深入學習線程模型

作者:IT程式設計學習棧

Dart 是一門支援異步和并發程式設計的語言,它提供了多種線程和協程的實作方式。下面從淺入深介紹 Dart 的線程相關知識。

1. 單線程模型

Dart 是一門單線程語言,所有的代碼都運作在一個單獨的主線程中。這個主線程又被稱為 UI 線程或者事件循環線程,因為它不僅負責執行 Dart 代碼,還要處理各種事件(比如滑鼠點選、鍵盤輸入等)和更新 UI。

例如,下面是一個簡單的 Dart 程式,它會輸出一條消息并等待使用者輸入:

import 'dart:io';

void main() {
  print('請輸入一些文本:');
  String text = stdin.readLineSync();
  print('你輸入的是:$text');
}           

在這個程式中,所有代碼都運作在主線程中,包括讀取使用者輸入的操作。當程式執行到 stdin.readLineSync() 這一行時,主線程就會阻塞,等待使用者輸入完成後才會繼續執行下去。

2. 異步程式設計

雖然 Dart 是一門單線程語言,但是它提供了多種異步程式設計的方式,可以讓我們編寫出高效、非阻塞的程式。下面分别介紹其中幾種方式:

2.1. Future 和 async/await

在 Dart 中,我們可以使用 Future 和 async/await 來實作異步程式設計。Future 表示一個異步操作的結果,可以用來處理網絡請求、檔案讀寫等 I/O 操作。async/await 則是一種文法糖,可以讓我們以同步的方式編寫異步代碼。

例如,下面是使用 Future 和 async/await 實作的與上面相同功能的程式:

import 'dart:io';

void main() async {
  print('請輸入一些文本:');
  String text = await stdin.readLine();
  print('你輸入的是:$text');
}           

在這個程式中,我們用 async 聲明了 main 函數是一個異步函數,然後使用 await 等待使用者輸入完成。當主線程遇到 await 這一行時,它會暫時中斷目前函數的執行,并去執行其他任務,直到異步操作完成後再恢複執行。

2.2. Stream 和 StreamBuilder

除了 Future,Dart 還提供了另外一種異步程式設計的方式——Stream。Stream 表示一個資料序列,可以用來處理一些需要持續接收資料的場景,比如 WebSocket、TCP 連接配接等。

對于 Stream,我們可以使用 StreamBuilder 來建構 UI,它可以自動更新 UI 的狀态,進而讓界面呈現出最新的資料。例如,下面是一個簡單的例子,它每隔 1 秒鐘輸出一個數字:

import 'dart:async';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: StreamBuilder<int>(
            stream: _getStream(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text('${snapshot.data}', style: TextStyle(fontSize: 32));
              } else {
                return CircularProgressIndicator();
              }
            },
          ),
        ),
      ),
    );
  }

  Stream<int> _getStream() {
    return Stream.periodic(Duration(seconds: 1), (num) => num).take(10);
  }
}           

在這個程式中,我們使用 Stream.periodic 來建立一個每隔 1 秒鐘發送一次資料的 Stream,然後使用 StreamBuilder 來将資料渲染到 UI 上。當 Stream 發送新的資料時,StreamBuilder 就會自動更新 UI 的狀态。

3. Isolate

雖然 Dart 是一門單線程語言,但是它提供了 Isolate 來支援多線程程式設計。一個 Isolate 就是一個獨立的運作環境,它有自己的記憶體空間、堆棧和指令計數器,可以并行地執行代碼。

在 Dart 中,我們可以使用 Isolate.spawn() 來建立一個新的 Isolate,例如:

import 'dart:isolate';

void main() async {
  ReceivePort receivePort = ReceivePort();
  Isolate isolate = await Isolate.spawn(_entryPoint, receivePort.sendPort);
  print('Isolate 已經啟動');
}

void _entryPoint(SendPort sendPort) {
  print('Isolate 開始運作');
}           

在這個程式中,我們先通過 ReceivePort 建立了一個端口,然後通過 Isolate.spawn() 建立了一個新的 Isolate,并将這個端口的發送端口作為參數傳遞給它。建立成功後,主線程就會繼續向下執行,而新的 Isolate 則會開始執行 _entryPoint 函數。

需要注意的是,不同的 Isolate 之間無法直接共享資料,它們隻能通過消息傳遞來進行通信。例如,我們可以在主線程中向另外一個 Isolate 發送一條消息:

import 'dart:isolate';

void main() async {
  ReceivePort receivePort = ReceivePort();
  Isolate isolate = await Isolate.spawn(_entryPoint, receivePort.sendPort);
  print('Isolate 已經啟動');

  receivePort.listen((message) {
    print('接收到消息:$message');
  });

  isolate.sendPort.send('你好,我是主線程!');
}

void _entryPoint(SendPort sendPort) {
  print('Isolate 開始運作');
  sendPort.send('你好,我是 Isolate!');
}           

在這個程式中,我們通過 sendPort 發送了一條消息給新建立的 Isolate,并通過 receivePort 監聽來自主線程的消息。當 Isolate 接收到消息時,它會列印出來,并通過 sendPort 回複一條消息給主線程。

以上就是 Dart 的線程相關知識的簡單介紹和代碼示例。需要注意的是,在使用多線程程式設計時,我們需要格外注意線程安全性和資料同步等問題,以避免出現不可預期的錯誤。

繼續閱讀