From 2f7a66386efa299f905b0f39f0175dbb8c5cfe1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Fri, 15 Jan 2021 22:40:26 +0100 Subject: [PATCH] docs: document `go` with handle and `wait()` (#8142) --- doc/docs.md | 67 +++++++++++++++++++++++++++++++++++++++++++++--- vlib/v/fmt/fmt.v | 10 +++++--- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/doc/docs.md b/doc/docs.md index 569efbd84b..2de7e7df37 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -96,7 +96,9 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h * [Option/Result types & error handling](#optionresult-types-and-error-handling) * [Generics](#generics) * [Concurrency](#concurrency) + * [Spawning Concurrent Tasks](#spawning-concurrent-tasks) * [Channels](#channels) + * [Shared Objects](#shared-objects) * [Decoding JSON](#decoding-json) * [Testing](#testing) * [Memory management](#memory-management) @@ -2191,10 +2193,67 @@ println(compare(1.1, 1.2)) // -1 ## Concurrency +### Spawning Concurrent Tasks +V's model of concurrency is very similar to Go's. To run `foo()` concurrently in +a different thread, just call it with `go foo()`: -V's model of concurrency is very similar to Go's. To run `foo()` concurrently, just -call it with `go foo()`. Right now, it launches the function on a new system -thread. Soon coroutines and a scheduler will be implemented. +```v +import math + +fn p(a f64, b f64) { // ordinary function without return value + c := math.sqrt(a * a + b * b) + println(c) +} + +fn main() { + go p(3, 4) + // p will be run in parallel thread +} +``` + +Sometimes it is necessary to wait until a parallel thread has finished. This can +be done by assigning a *handle* to the started thread and calling the `wait()` method +to this handle later: + +```v +import math + +fn p(a f64, b f64) { // ordinary function without return value + c := math.sqrt(a * a + b * b) + println(c) // prints `5` +} + +fn main() { + h := go p(3, 4) + // p() runs in parallel thread + h.wait() + // p() has definitely finished +} +``` + +This approach can also be used to get a return value from a function that is run in a +parallel thread. There is no need to modify the function itself to be able to call it +concurrently. + +```v +import math { sqrt } + +fn get_hypot(a f64, b f64) f64 { // ordinary function returning a value + c := sqrt(a * a + b * b) + return c +} + +fn main() { + g := go get_hypot(54.06, 2.08) // spawn thread and get handle to it + h1 := get_hypot(2.32, 16.74) // do some other calculation here + h2 := g.wait() // get result from spawned thread + println('Results: $h1, $h2') // prints `Results: 16.9, 54.1` +} +``` + +If there is a large number of tasks that do not return a value it might be easier to manage +them using a wait group. However, for this approach the function(s) called concurrently have +to be designed with this wait group in mind: ```v import sync @@ -2369,6 +2428,8 @@ the reason why not. Usage of these methods and properties in production is not recommended - algorithms based on them are often subject to race conditions. Use `select` instead. +### Shared Objects + Data can be exchanged between a coroutine and the calling thread via a shared variable. Such variables should be created as `shared` and passed to the coroutine as such, too. The underlying `struct` contains a hidden *mutex* that allows locking concurrent access diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index bb02a37fb7..8ae39ce69f 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -404,7 +404,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { f.global_decl(node) } ast.GoStmt { - f.go_stmt(node) + f.go_stmt(node, false) } ast.GotoLabel { f.writeln('$node.name:') @@ -893,7 +893,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) { f.write(node.val) } ast.GoExpr { - f.stmt(node.go_stmt) + f.go_stmt(node.go_stmt, true) } ast.IfExpr { f.if_expr(node) @@ -2294,10 +2294,12 @@ pub fn (mut f Fmt) for_stmt(node ast.ForStmt) { f.writeln('}') } -pub fn (mut f Fmt) go_stmt(node ast.GoStmt) { +pub fn (mut f Fmt) go_stmt(node ast.GoStmt, is_expr bool) { f.write('go ') f.expr(node.call_expr) - f.writeln('') + if !is_expr { + f.writeln('') + } } pub fn (mut f Fmt) return_stmt(node ast.Return) {