Parallele Programmierung (3IB)
run
-Methode): stehen für sichStack:
Stack:
fac(4,1)
: die beiden Parameter + Platz für den RückgabewertStack:
fac(4-1, 1*4)
Stack:
fac(3-1, 4*3)
Stack:
fac(2-1, 12*2)
Stack:
acc
Stack:
acc
Stack:
acc
Stack:
acc
call-with-current-continuation
(oft abgekürzt als call/cc
) oder yield
yield
in Java anders!Das hat erstmal nichts mit paralleler Programmierung zu tun!
Stoll (1985)
Das ist Nebenläufigkeit ohne Parallelität!
yield
zum Scheduler wünschenswertUnterprogramm
kann geschachtelte Unteraufrufe von Subroutinen beinhalten
wird grundsätzlich immer am Anfang begonnen
wird immer “zum Ende” verlassen (bei jedem return
)
Kontext wird auf Call Stack des Aufrufer-Threads gespeichert
yield
)synchronized
, Locks, Atomics
1
2
3
4711
4
5
6
async
und await
SchlüsselworteFuture.delayed
erzeugt ein (Dart-) Future, das nach einer vorgegebenen Verzögerung läuft.Future.value(...)
führt ...
asynchron ausVerkettung wie bei CompletableFuture
(in Java):
Iterable
async*
next()
ist Wechsel zur nächsten Fiber (z.B. eine andere Generator Function) möglichStream<int> timedCounter(Duration interval, [int? maxCount]) async* {
int i = 0;
while (true) {
await Future.delayed(interval);
yield i++;
if (i == maxCount) break;
}
}
Stream<T> streamFromFutures<T>(Iterable<Future<T>> futures) async* {
for (final future in futures) {
var result = await future;
yield result;
}
}
Ähnlich wie bei Node.js gibt es eine zentrale Ereignis-Warteschlange, die zyklisch geupdated wird.
Alle I/O-Operationen sind asynchron:
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
final process = await Process.start('ls', ['-l']);
final lineStream = process.stdout
.transform(const Utf8Decoder())
.transform(const LineSplitter());
await for (final line in lineStream) {
print(line);
}
await process.stderr.drain();
print('exit code: ${await process.exitCode}');
}
Virtual Threads (“Fibers”)
Structured Concurrency
StructuredTaskScope
Executor
ThreadFactory
public class Exec<T> {
static <T> T race(List<Callable<T>> tasks)
throws InterruptedException, ExecutionException {
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<T>()) {
for (var task : tasks) {
scope.fork(task);
}
return scope.join().result();
}
}
public static void main(String... args)
throws InterruptedException, TimeoutException, Throwable {
var tasks = new ArrayList<Callable<BigInteger>>();
for (var i=1; i < Runtime.getRuntime().availableProcessors(); i++) {
tasks.add(() -> BigInteger.probablePrime(2048, ThreadLocalRandom.current()));
}
System.out.println(race(tasks));
}
}
Runnable runnable = () -> System.out.println(Thread.currentThread());
var virtualThreadFactory = Thread.Builder.Virtual.factory();
var kernelThreadFactory = Thread.Builder.factory();
var virtualThread = virtualThreadFactory.newThread(runnable);
var kernelThread = kernelThreadFactory.newThread(runnable);
virtualThread.start();
kernelThread.start();
Executors.defaultThreadFactory()
Anwendung des Builder Patterns:
var t1 = Thread.ofVirtual()
.name("Mein Thread")
.allowSetThreadLocals(true)
.uncaughtExceptionHandler(
(t e) -> /*...*/
)
.unstarted(runnable)
.start();
ofVirtual()
liefert Thread.Builder.OfVirtual
unstarted()
liefert dann Thread
(quasi build-Methode)start()
aufrufen