docs: document `go` with handle and `wait()` (#8142)

pull/8164/head
Uwe Krüger 2021-01-15 22:40:26 +01:00 committed by GitHub
parent 1d1fda9522
commit 2f7a66386e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 7 deletions

View File

@ -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

View File

@ -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) {