Что не так с потоками?
С "потоками" всё так, просто вы неправильно работаете с "массивом".

perftracing = append(perftracing, 0) при исчерпании выделенной памяти выделяет новый блок памяти (по-моему удвоенного размера) и копирует текущие элементы в новый блок. Соответственно часть (половина) горутин пишет по старым адресам, а в выхлопе данные из последнего выделенного блока памяти.

...
0 0 0 0 5197273 5671085 5177800 5797080 
0 0 0 0 5197332 5671147 5177908 5797137 
0 0 0 0 5197388 5671208 5178008 5797195 
0 0 0 0 5197450 5671269 5178118 5797248
...

Если создать "массив" заранее со всеми элементами, то всё выводится нормально:
threads := 4
perftracing := make([]int64, threads)
for i := 0; i < threads; i++ {
	go thread(&perftracing[i])
	time.Sleep(1)
}

...
5593758 7107822 5575001 6449763 6321574 5535152 5728000 7196092 
5593813 7107884 5575056 6449859 6321628 5535209 5728060 7196153 
5593879 7107959 5575122 6449976 6321695 5535275 5728122 7196217 
5593936 7108030 5575180 6450079 6321757 5535339 5728190 7196285 
...
5594008 7108109 5575251 6450199 6321821 5535403 5728251 7196349

Ну и вместо time.Sleep(1) вы наверно имели в виду что-то типа time.Sleep(time.Second * 1)

Go 1.19.3, Kubuntu 22.04
непонятной причине потоки (goroutines) сами по себе перестают выполняться.
В каждом потоке может быть > 1 корутины. С точки зрения ОС выделяются потоки, а как там внутри работает - это ответственность рантайма Го. Выделяется то количество потоков ОС, которое рантайм посчитает достаточным.