Concurrency
New in 0.3.0: spawn starts a thread, and
channels carry values between threads. Sure's concurrency is fearless*,
for the same reason everything else about it is.
spawn
spawn f(args) runs the call on its own operating-system
thread and moves on immediately. Any of your functions can be spawned;
its return value, if any, goes nowhere. There is no join and no thread
handle — if you want to know a thread finished, it can tell you, on a
channel.
Channels
A channel of T has type chan T. Like an
empty collection, a new channel learns its type from its declaration;
the optional argument is a buffer capacity.
func shout(inbox: chan String) {
print("received: " + recv(inbox))
}
func main() -> Int {
let inbox: chan String = channel() // unbuffered
spawn shout(inbox)
send(inbox, "hello from the main thread")
return 0
}
send(c, v)delivers a value. On an unbuffered channel it waits until a receiver has actually taken it — a rendezvous, not a mailbox. On a buffered channel (channel(16)) it waits only when the buffer is full.recv(c)waits for a value. Receiving from a closed, empty channel ends the program:sure: this channel has nothing left to say. Loops should preferfor.close(c)announces that no more values are coming. Closing is the sender's job. Sending after close ends the program:the message has nowhere to go.
for, over a channel
The idiomatic receive loop is for, which takes values
until the channel closes and then simply ends — no abort, no
sentinel:
func squares(nums: chan Int, n: Int) {
for i in 1..n + 1 {
send(nums, i * i)
}
close(nums)
}
func report(nums: chan Int, done: chan String) {
var total = 0
for v in nums {
total = total + v
}
send(done, "total: " + str(total))
}
func main() -> Int {
let nums: chan Int = channel(4)
let done: chan String = channel()
spawn squares(nums, 5)
spawn report(nums, done)
print(recv(done)) // total: 55
return 0
}
This is the pipeline pattern, and it is most of what you need:
producers send, consumers for, the final answer arrives on
a channel that main is holding. Note how
main waits — not with a join, but by receiving. In Sure
you wait for work the same way you get its results, which keeps the
design honest about which is which.
Share memory by communicating
Nothing stops you from handing the same array to two spawned functions. The language will compile it; the threads will interleave; the result will be what it will be. Sure has no locks to offer you and no intention of acquiring any — the supported design is the one you just read: give each thread its own values and pass what must travel through channels. A value that has been sent should be treated as gone from the sender's world; it changed hands at the moment of the send, and chapter 7's machinery schedules both sides accordingly.
sure blame will later explain to
you with great confidence.