Parallelität und Multithreading in TypeScript: Von der Theorie zur Praxis
Moderne Webanwendungen stehen zunehmend vor der Notwendigkeit, große Datenmengen zu verarbeiten und rechenintensive Operationen durchzuführen, ohne die Benutzeroberfläche zu blockieren. TypeScript, als Erweiterung von JavaScript, erbt dessen Single-Thread-Ausführungsmodell, bietet aber dennoch leistungsstarke Werkzeuge zur Organisation von Parallelität und Multithreading. In diesem Artikel werden wir uns ansehen, wie Sie diese Möglichkeiten effektiv in Ihren Projekten nutzen können.
Verständnis von Parallelität und Multithreading
Bevor wir uns in den Code vertiefen, ist es wichtig, zwei Schlüsselbegriffe voneinander abzugrenzen:
- Parallelität (Concurrency) — die Fähigkeit eines Systems, mehrere Aufgaben in überlappenden Zeiträumen zu bearbeiten, aber nicht unbedingt gleichzeitig. Dies wird durch schnellen Kontextwechsel erreicht.
- Multithreading — die parallele Ausführung mehrerer Threads auf verschiedenen Prozessorkernen. In Node.js wird dies durch Worker Threads realisiert.
TypeScript läuft in der Node.js-Umgebung, wo der Hauptthread Ereignisse über die Event Loop verarbeitet. Für schwere Berechnungen kann dies zu einem Engpass werden.
Asynchronität und Promise: Die Grundlage der Parallelität
Die grundlegende Ebene der Parallelität in TypeScript basiert auf async/await und Promise. Diese Mechanismen ermöglichen es, den Hauptthread während des Wartens auf I/O-Operationen nicht zu blockieren.
// Beispiel für asynchrones Laden von Dateninterface User { id: number; name: string;}
async function fetchUser(id: number): Promise<User> { const response = await fetch(`https://api.example.com/users/${id}`); return response.json();}
// Paralleler Start mehrerer Anfragenasync function loadUsers(): Promise<User[]> { const userIds = [1, 2, 3, 4, 5]; const promises = userIds.map(id => fetchUser(id)); return Promise.all(promises);}
loadUsers().then(users => { console.log('Geladene Benutzer:', users.length);});Promise.all startet alle Promises gleichzeitig, aber sie werden in einem einzigen Thread ausgeführt. Dies ist ideal für I/O-Aufgaben, aber nicht geeignet für CPU-intensive Operationen.
Worker Threads: Echtes Multithreading
Für die Ausführung schwerer Berechnungen ohne Blockierung des Hauptthreads verwenden Sie Worker Threads. Dies ist ein integriertes Modul von Node.js, das separate Threads mit eigenem Speicher erstellt.
Erstellen wir ein Beispiel zur Berechnung von Fibonacci-Zahlen in einem separaten Thread:
// fibonacci.worker.tsimport { parentPort } from 'worker_threads';
function fibonacci(n: number): number { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2);}
parentPort?.on('message', (n: number) => { const result = fibonacci(n); parentPort?.postMessage(result);});// main.tsimport { Worker } from 'worker_threads';import path from 'path';
function runFibonacciInWorker(n: number): Promise<number> { return new Promise((resolve, reject) => { const worker = new Worker(path.resolve(__dirname, 'fibonacci.worker.ts')); worker.postMessage(n); worker.on('message', resolve); worker.on('error', reject); worker.on('exit', (code) => { if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`)); }); });}
async function main() { console.time('fibonacci'); const result = await runFibonacciInWorker(40); console.timeEnd('fibonacci'); console.log(`Fibonacci(40) = ${result}`);}
main();Beachten Sie: Zum Ausführen von TypeScript-Workern ist eine Kompilierung in JavaScript erforderlich. Sie können ts-node oder eine vorherige Kompilierung verwenden.
SharedArrayBuffer: Gemeinsamer Speicher zwischen Threads
Wenn Threads große Datenmengen austauschen müssen