> There is no way for your operating system to know exactly how much stack space a thread will need so it allocates an amount on the order of around a megabyte. You only have around a bakers dozen gigabytes of RAM, so you can only make give or take 10,000 threads.<p>That's not true at all. The megabyte(s) of stack space is virtual, spawning a thread only requires a few kilobytes of memory initially, and is a lot cheaper than many realize.
People sometimes respond with some puzzlement to InterruptedException, unsure what they should do. All it means is that some other thread has requested that this thread stop what it's doing. So do what you think your code should do in that situation. E.g. in the case of `say`, the most reasonable thing is to simply return.<p>You shouldn't normally call `Thread.currentThread().interrupt()` inside a `catch (InterruptedException e)` unless, perhaps, you don't throw anything. That would prevent any blocking operation inside cleanup code from working. As a general rule, a thread's interrupt status is set <i>or</i> InterruptedException (or some other exception) is thrown, but not both. That is how JDK methods also do it. If they throw — they clear the interrupt status.
While there are use cases where lightweight threads are a benefit, I think a web crawler is pretty much the ideal case for async/await, given you are almost exclusively waiting. Although I must admit I don't really understand this example, why is the fetching being parallellized, like you typically want to add sleeps, even with a single thread you've got yourself a gatling gun that can DoS a website if you aren't careful.<p>There's also some optimizations that could be made. Instead of having<p><pre><code> synchronized (seen) {
if (!seen.contains(u)) {
seen.add(u);
// ...
}
}
</code></pre>
you can get rid of the synchronization with<p><pre><code> // seen = ConcurrentHashMap.newKeySet()
if (seen.add(u)) {
// ...
}</code></pre>
In Go, the select branch to take is non-deterministically chosen from the set of enabled branches.
For the examples I don't think it makes a difference, but in general you cannot assume that a select attempts to choose branches from top to bottom.
Loom's virtual thread are much more similar to GHC's threads than to go routines. And GHC Haskell is also able to spawn millions of threads, trivially.
> In Go there is less noise, but also there no way to interrupt Go's time.Sleep.<p>You can do so in one of two ways using the context API (<a href="https://go.dev/blog/context" rel="nofollow">https://go.dev/blog/context</a>) while keeping the code synchronous and idiomatic:<p><pre><code> package main
import (
"context"
"fmt"
"time"
)
func say(ctx context.Context, s string) {
for i := 0; i < 5; i++ {
select {
case <-time.After(100 * time.Millisecond):
fmt.Println(s)
case <-ctx.Done():
return // Operation canceled or timed out.
}
}
}
func main() {
ctx := context.Background() // Use context cancellation semantics.
go say(ctx, "world")
say(ctx, "hello")
}
</code></pre>
If the code is reasonably performance sensitive AND there's a high degree of likelihood the context is canceled before the operation completes, you can use a time.Timer similar to what follows:<p><pre><code> for i := 0; i < 5; i++ {
func(i int) { // Wrap in closure expressly to support defer statement.
t := time.NewTimer(100 * time.Millisecond)
defer t.Stop()
select {
case <-t.C:
fmt.Println(s)
case <-ctx.Done():
return // Operation canceled or timed out.
}
}(i)
}</code></pre>
Go solution to TreeWalk problem is wrong, or, rather, not real-world, because it has a memory leak. You need to make sure all goroutines finish, and not stuck on reading/writing to a channel.<p>In Kotlin coroutines you can .cancel() a coroutine, but Go doesn't allow to cancel goroutines.
time.Sleep(100 * time.Millisecond) could also be: <-time.After(100 * time.Millisecond) so that an imaginary timeout can be easily handled with a select.
Good read! I just saw that in one examples you create and start a thread by invoking #startVirtualThread, and then you call start on the returned thread again.