Что не так с потоками?

По непонятной причине потоки (goroutines) сами по себе перестают выполняться. А именно работает только половина потоков, если даже создать только два. Функцию runtime.GOMAXPROCS() пробовал, ничего не меняется.
Хотя на скриншоте процессор загружен полностью, если создать 56 потоков.
package main
import (
	"math"
	"time"
)
func thread(x *int64) {
	print("thread")
	for {
		math.Sqrt(math.Pow(1999990093912455, 234))
		math.Sqrt(math.Pow(1999990093912455, 234))
		math.Sqrt(math.Pow(1999990093912455, 234))
		math.Sqrt(math.Pow(1999990093912455, 234))
		math.Sqrt(math.Pow(1999990093912455, 234))
		math.Sqrt(math.Pow(1999990093912455, 234))
		*x++
	}
}

func main() {
	perftracing := []int64{}
	threads := 2
	for i := 0; i < threads; i++ {
		perftracing = append(perftracing, 0)
		go thread(&perftracing[i])
		time.Sleep(1)
	}
	for { //цикл чтобы не завершался основной процесс
		time.Sleep(1) 
		for i := 0; i < threads; i++ { //цикл чтобы следить за выполнением
			print(perftracing[i], " ")
		}
		println()
	}
}

6367e1226d111257439225.jpeg
  • Вопрос задан
  • 314 просмотров
Решения вопроса 1
@falconandy
С "потоками" всё так, просто вы неправильно работаете с "массивом".

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
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
Рантайм го расчитан на то, что большинство горутин будут иметь точки блокировок, что логично для веб-сервисов. Когда сервис блокируется на чтении/записи сети/файла, планировщик переключается на другую горутину. Поэтому нет никаких гарантий того, что нагрузка равномерно распределиться, если все горутины будут заняты просто числомолотилками.
После какой-то версии го (точно не помню номер), завезли вытесняющую многозадачность и теперь планировщик может переключать горутины даже на числомолотилках, но делает он это обычно когда запускается сборка мусора или срабатывает случайный таймер. Поэтому равномерного распределения на таких задачах не ждите, язык просто делался не для этого.
Ответ написан
Комментировать
непонятной причине потоки (goroutines) сами по себе перестают выполняться.

В каждом потоке может быть > 1 корутины. С точки зрения ОС выделяются потоки, а как там внутри работает - это ответственность рантайма Го. Выделяется то количество потоков ОС, которое рантайм посчитает достаточным.
Ответ написан
Комментировать
@AlekseiBadyaev
CTO, Skala Software
Для ответа на вопрос нужна дополнительная информация: как именно было установлено, что "потоки (goroutines) сами по себе перестают выполняться" и "работает только половина потоков, если даже создать только два" ?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы