docs: document `go` with handle and `wait()` (#8142)
parent
1d1fda9522
commit
2f7a66386e
67
doc/docs.md
67
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)
|
* [Option/Result types & error handling](#optionresult-types-and-error-handling)
|
||||||
* [Generics](#generics)
|
* [Generics](#generics)
|
||||||
* [Concurrency](#concurrency)
|
* [Concurrency](#concurrency)
|
||||||
|
* [Spawning Concurrent Tasks](#spawning-concurrent-tasks)
|
||||||
* [Channels](#channels)
|
* [Channels](#channels)
|
||||||
|
* [Shared Objects](#shared-objects)
|
||||||
* [Decoding JSON](#decoding-json)
|
* [Decoding JSON](#decoding-json)
|
||||||
* [Testing](#testing)
|
* [Testing](#testing)
|
||||||
* [Memory management](#memory-management)
|
* [Memory management](#memory-management)
|
||||||
|
@ -2191,10 +2193,67 @@ println(compare(1.1, 1.2)) // -1
|
||||||
|
|
||||||
|
|
||||||
## Concurrency
|
## 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
|
```v
|
||||||
call it with `go foo()`. Right now, it launches the function on a new system
|
import math
|
||||||
thread. Soon coroutines and a scheduler will be implemented.
|
|
||||||
|
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
|
```v
|
||||||
import sync
|
import sync
|
||||||
|
@ -2369,6 +2428,8 @@ the reason why not.
|
||||||
Usage of these methods and properties in production is not recommended -
|
Usage of these methods and properties in production is not recommended -
|
||||||
algorithms based on them are often subject to race conditions. Use `select` instead.
|
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.
|
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.
|
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
|
The underlying `struct` contains a hidden *mutex* that allows locking concurrent access
|
||||||
|
|
|
@ -404,7 +404,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
|
||||||
f.global_decl(node)
|
f.global_decl(node)
|
||||||
}
|
}
|
||||||
ast.GoStmt {
|
ast.GoStmt {
|
||||||
f.go_stmt(node)
|
f.go_stmt(node, false)
|
||||||
}
|
}
|
||||||
ast.GotoLabel {
|
ast.GotoLabel {
|
||||||
f.writeln('$node.name:')
|
f.writeln('$node.name:')
|
||||||
|
@ -893,7 +893,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||||
f.write(node.val)
|
f.write(node.val)
|
||||||
}
|
}
|
||||||
ast.GoExpr {
|
ast.GoExpr {
|
||||||
f.stmt(node.go_stmt)
|
f.go_stmt(node.go_stmt, true)
|
||||||
}
|
}
|
||||||
ast.IfExpr {
|
ast.IfExpr {
|
||||||
f.if_expr(node)
|
f.if_expr(node)
|
||||||
|
@ -2294,10 +2294,12 @@ pub fn (mut f Fmt) for_stmt(node ast.ForStmt) {
|
||||||
f.writeln('}')
|
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.write('go ')
|
||||||
f.expr(node.call_expr)
|
f.expr(node.call_expr)
|
||||||
f.writeln('')
|
if !is_expr {
|
||||||
|
f.writeln('')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut f Fmt) return_stmt(node ast.Return) {
|
pub fn (mut f Fmt) return_stmt(node ast.Return) {
|
||||||
|
|
Loading…
Reference in New Issue