I’m wondering is there any way to create a singe-threaded console application in Go?
Surely, I completely understand this is not the Golang way to do things (and we can create such application in C/Rust/Zig/&c), but I’m just exploring the abilities of Go language and its modern compilers.
I have tried different approaches involving runtime.GOMAXPROCS(), but seems it doesn’t affect the overall number of threads with the values lower than current CPU count (or I’m just doing it wrong).
Basically, I wanted to play with creating some old-style console application for Linux without any concurrency and goroutines, just an infinite for loop doing some things periodically (maybe reading some files in I/O-blocking way and display some info from them), where I occasionally call the garbage collector manually if I need to.
As I understand, I can turn off the garbage collector and run it manually with debug.SetGCPercent, debug.SetMemoryLimit, and runtime.GC(). But how can I create single-threadness? Or is it impossible intrinsically?
Here’s my basic draft:
package main
import (
"runtime"
"runtime/debug"
"strings"
"time"
)
func init() {
// Locking OS thread
runtime.LockOSThread()
// GOMAXPROCS=1, Any effect?
runtime.GOMAXPROCS(1)
// Turn off garbage collection
debug.SetGCPercent(-1)
//debug.SetMemoryLimit(math.MaxInt64)
}
func main() {
var index uint64
for {
index++
// Emulate some memory allocation
strings.Repeat("x", 10*1024)
// Get memory statistics
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
// Output some info
println(index, memStats.Alloc/1024.0, memStats.Sys/1024.0, memStats.NumGC)
if index%20 == 0 {
// Run GC periodically
runtime.GC()
}
time.Sleep(time.Duration(500) * time.Millisecond)
}
}
Build for Linux:
GOARCH=amd64 GOOS=linux go build -ldflags "-s -w" -trimpath .
Even though, I don’t use goroutines and have set GOMAXPROCS to 1, I have multiple threads in the OS (Debian/Ubuntu in this case). Here’s the htop output:
I realize the runtime reserves at least one thread for the application, one for the garbage collector, and a couple more just in case. The question is, is this behavior mandatory, or can it be disabled somehow? (Let’s say I want to shoot myself in the foot and handle all memory and I/O-blocking issues by myself.)
I’m wondering is there any way to create a singe-threaded console application in Go?
Surely, I completely understand this is not the Golang way to do things (and we can create such application in C/Rust/Zig/&c), but I’m just exploring the abilities of Go language and its modern compilers.
I have tried different approaches involving runtime.GOMAXPROCS(), but seems it doesn’t affect the overall number of threads with the values lower than current CPU count (or I’m just doing it wrong).
Basically, I wanted to play with creating some old-style console application for Linux without any concurrency and goroutines, just an infinite for loop doing some things periodically (maybe reading some files in I/O-blocking way and display some info from them), where I occasionally call the garbage collector manually if I need to.
As I understand, I can turn off the garbage collector and run it manually with debug.SetGCPercent, debug.SetMemoryLimit, and runtime.GC(). But how can I create single-threadness? Or is it impossible intrinsically?
Here’s my basic draft:
package main
import (
"runtime"
"runtime/debug"
"strings"
"time"
)
func init() {
// Locking OS thread
runtime.LockOSThread()
// GOMAXPROCS=1, Any effect?
runtime.GOMAXPROCS(1)
// Turn off garbage collection
debug.SetGCPercent(-1)
//debug.SetMemoryLimit(math.MaxInt64)
}
func main() {
var index uint64
for {
index++
// Emulate some memory allocation
strings.Repeat("x", 10*1024)
// Get memory statistics
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
// Output some info
println(index, memStats.Alloc/1024.0, memStats.Sys/1024.0, memStats.NumGC)
if index%20 == 0 {
// Run GC periodically
runtime.GC()
}
time.Sleep(time.Duration(500) * time.Millisecond)
}
}
Build for Linux:
GOARCH=amd64 GOOS=linux go build -ldflags "-s -w" -trimpath .
Even though, I don’t use goroutines and have set GOMAXPROCS to 1, I have multiple threads in the OS (Debian/Ubuntu in this case). Here’s the htop output:
I realize the runtime reserves at least one thread for the application, one for the garbage collector, and a couple more just in case. The question is, is this behavior mandatory, or can it be disabled somehow? (Let’s say I want to shoot myself in the foot and handle all memory and I/O-blocking issues by myself.)
So you want to get rid of all things good in Go and make it behave like an oldschool C program?
package main
import (
"fmt"
"os"
"runtime"
"runtime/debug"
)
func main() {
runtime.GOMAXPROCS(1) // limit to one logical processor
runtime.LockOSThread() // pin execution to a single OS thread
debug.SetGCPercent(-1) // disable automatic GC
fmt.Println("good old day feelings start here")
// ... use syscall-based I/O for more nostalgia
}
HOWEVER, Go always spawns additional threads even when GOMAXPROCS(1) is set. This is because the runtime manages scheduling, system calls, signaling, and garbage collection (also: kernel threads for blocking syscalls or eg using Cgo). Not using something doesn't make it disappear.
=> this is probably as far as "faking it" can take you ... but why !?!
