Здравствуйте, mrTwister, Вы писали:
T>Нет конечно, time.Sleep создает под капотом таймер, который при срабатывании вызывает continuation у горутины. В зависимости от того, в каком порядке сработали эти таймеры, получится разный порядок цифр.
Чем это лучше запуска в ThreadPool — и через ThreadPool и через go — нельзя простым образом получить результат.
А то что все функции в go асинхронные — вы не доказали. Жду доказательство.
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, mrTwister, Вы писали:
S>Как ты это сможешь доказать? Ок, c go — асинхронный, верю. А без go — какие ваши доказательства что оно асинхрон?
T>>и несмотря на запуск 10 тасок/10 горутин, будет по факту использовано скорее не 10 потоков, а меньше.
S>Так это и в C# — там ThreadPool.
Я это и написал
T>>Таким образом оба варианта можно, например, безопасно вызывать в http хендлере не опасаясь заблокировать работу http сервера из-за thread pool depletion
S>Это не доказательство — завимсит от реализации обработки http.
Современный http сервер на .net поддерживает асинхронность
S>Давай доказательство на твоем коде.
Запусти его с GOMAXPROCS=1, в этом случае все выполнится строго в одном потоке
S>Это вы реализовали функционал async|await, но весьма криво — через каналы. Это раньше приводил вам.
В чем кривость?
Здравствуйте, mrTwister, Вы писали:
S>>Давай доказательство на твоем коде. T>Запусти его с GOMAXPROCS=1, в этом случае все выполнится строго в одном потоке
Вопрос такой: я убедился что go запускает в пулле потоков. А как мне убедиться что без go тоже выполняется асинхронно а не обычным синхронным образом?
Даже тут у тебя идентификаторы потоков повторяются, а значит для каждой горутины не создается отдельный поток
S>Чем ваше go отличается от запуска в ThreadPool?
Тем, что в go горутина не может заблокировать поток, благодаря этому thread pool в go фиксированного размера, по умолчанию в нем число потоков равно числу ядер и новые потоки не добавляются, потому что это не надо. Благодаря этому в го нельзя заблокировать асинхронный контекст и избежать проблем: https://rsdn.org/forum/flame.comp/9030348.1
В go ты можешь иметь в тредпуле 1 поток и запустить 100000 конкурентных горутин, которые будут выполняться "одновременно" на этом единственном потоке. Если ты в C# запустишь 100000 Task.Run(async () => while(true) {}), то у тебя все встанет раком. А в go горутины будут одновременно работать как ни в чем не бывало на одном единственном потоке.
Здравствуйте, Shmj, Вы писали:
T>>Запусти его с GOMAXPROCS=1, в этом случае все выполнится строго в одном потоке
S>Вопрос такой: я убедился что go запускает в пулле потоков. А как мне убедиться что без go тоже выполняется асинхронно а не обычным синхронным образом?
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, mrTwister, Вы писали:
T>>Нет конечно, time.Sleep создает под капотом таймер, который при срабатывании вызывает continuation у горутины. В зависимости от того, в каком порядке сработали эти таймеры, получится разный порядок цифр.
S>Чем это лучше запуска в ThreadPool — и через ThreadPool и через go — нельзя простым образом получить результат.
Тем, что во время ожидания не блокируется поток. Ты же почему-то написал "await Task.Delay(...)", а не "Thread.Sleep(...)", почему?
S>А то что все функции в go асинхронные — вы не доказали. Жду доказательство.
Как ты объяснишь, что ThreadID у горутин повторялся, несмотря на то, что они работали одновременно (висели в time.Sleep)?
Здравствуйте, mrTwister, Вы писали:
S>>Вопрос такой: я убедился что go запускает в пулле потоков. А как мне убедиться что без go тоже выполняется асинхронно а не обычным синхронным образом? T>Я же написал: запусти его с GOMAXPROCS=1
Здравствуйте, Shmj, Вы писали:
T>>Я же написал: запусти его с GOMAXPROCS=1
S>И что должно быть?
Всегда будет один ThreadID, при том, что запущенные функции работают одновременно. Это возможно только если выполняемые в горутинах функции асинхронны.
Здравствуйте, mrTwister, Вы писали:
T>Всегда будет один ThreadID, при том, что запущенные функции работают одновременно. Это возможно только если выполняемые в горутинах функции асинхронны.
Т.е. при вызове через go. А как убедиться что асинхрон и без вызова через go?
T>В go ты можешь иметь в тредпуле 1 поток и запустить 100000 конкурентных горутин, которые будут выполняться "одновременно" на этом единственном потоке. Если ты в C# запустишь 100000 Task.Run(async () => while(true) {}), то у тебя все встанет раком. А в go горутины будут одновременно работать как ни в чем не бывало на одном единственном потоке.
.Net Использует пул потоков. Для долгих задач есть опция LongRunning
Здравствуйте, Shmj, Вы писали: S>Здравствуйте, mrTwister, Вы писали: T>>Всегда будет один ThreadID, при том, что запущенные функции работают одновременно. Это возможно только если выполняемые в горутинах функции асинхронны. S>Т.е. при вызове через go. А как убедиться что асинхрон и без вызова через go?
package main
import (
"syscall"
"time"
)
func Foo() {
println("Foo started at thead #", syscall.Gettid())
time.Sleep(time.Millisecond * 100)
println("Foo finished at thead #", syscall.Gettid())
}
func main() {
for range 10 {
go func() {
time.Sleep(time.Millisecond * 100)
}()
}
Foo()
}
Я функцию Foo запускаю без всяких горутин, а просто "Foo()".
Программа вывела:
Foo started at thead # 11
Foo finished at thead # 14
То есть функция начала работать на одном потоке ОС, а закончила на другом потоке. Как это возможно, если бы она не была асинхронной?
Параллельно я запустил еще десяток горутин без функции Foo, чтобы переезд на тругой тред чаще воспроизводился. Оно воспроизводится и без этого, но не каждый раз, надо просто чаще позапускать (иногда раз 10): https://go.dev/play/p/087bV3w7fMT
Здравствуйте, mrTwister, Вы писали:
S>>.Net Использует пул потоков. Для долгих задач есть опция LongRunning
T>Я в курсе. Проблемы подхода .net описывал тут: T>https://rsdn.org/forum/flame.comp/9030348.1
Угу решение все асинхронные операции выполнять в одном потоке не есть правильное решение или все сводить к функциональщине на иммутабельных объектах.
На C# можешь делать все на одном потоке со своим планировщиком.
LongRunning не зря придуман. Для оптимального программирования нужны различные инструменты.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, mrTwister, Вы писали:
T>То есть функция начала работать на одном потоке ОС, а закончила на другом потоке. Как это возможно, если бы она не была асинхронной?
Простите, но это уличная магия, а не асинхронность. Так работает шедулер и переключение контекста. После thread park (то что делает sleep) шедулер может продолжить исполнение на другом ядре. Это многопоточность, а не асинхронность.
Асинхронный это если части кода выполняются одновременно. Примерно так, код, ясен пень, неработающий:
func func1() int {
// какой-нибудь сетевой вызов, который длится 3 секунды
time.Sleep(3 * time.Second)
return 1;
}
func func2() int {
// какой-нибудь сетевой вызов, который длится 5 секунд
time.Sleep(5 * time.Second)
return 2;
}
val1 := func1() // "асинхронный" вызов 1 (на самом деле, конечно же нет)
val2 := func2() // "асинхронный" вызов 2 (на самом деле, конечно же нет)
fmt.Println(val1 + val2) // используем результаты и комбинируем
// вот это всё должно отработать за 5 секунд, а не за 8.
Именно для такого придумали async/await. Но выглядит отстойно и прошлый век. Собственно поэтому и сабж.
Впрочем, в гошке такое будет ещё хуже, особенно если озаботиться обработкой ошибок.
Самый вменяемый код — это на Java:
Здравствуйте, Serginio1, Вы писали:
S>·>val1 := func1() // "асинхронный" вызов 1 (на самом деле, конечно же нет) S>·>val2 := func2() // "асинхронный" вызов 2 (на самом деле, конечно же нет) S> C# это Task.WhenAll
Зачем ты это мне рассказываешь, да ещё и фигню невтемную говоришь? В данном случае будет как-то так:
var r1 = func1();
var r2 = func2();
System.Console.WriteLine(await(r1) + await(r2));
И эти функции ДОЛЖНЫ быть объявлены как async и быть написаны правильно на всю глубину вызовов, протаскивая async повсюду. Сабж!
Это же полная Ж по сравнению с java.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, mrTwister, Вы писали:
T>>То есть функция начала работать на одном потоке ОС, а закончила на другом потоке. Как это возможно, если бы она не была асинхронной? ·>Простите, но это уличная магия, а не асинхронность. Так работает шедулер и переключение контекста. После thread park (то что делает sleep) шедулер может продолжить исполнение на другом ядре. Это многопоточность, а не асинхронность.
Шедулер может продолжить выполнение на другом ядре, а на другом потоке не может. Тут же выполнение происходит на другом потоке, а не ядре. Чтобы шедулер такое сделал функция обязана быть асинхронной и предоставлять промис для продолжения
·>Асинхронный это если части кода выполняются одновременно. Примерно так, код, ясен пень, неработающий:
Делается тривиальный хелпер на три строчки и код будет выглядеть точно так же
func func1() int {
// какой-нибудь сетевой вызов, который длится 3 секунды
time.Sleep(3 * time.Second)
return 1
}
func func2() int {
// какой-нибудь сетевой вызов, который длится 5 секунд
time.Sleep(5 * time.Second)
return 2
}
func main() {
val1 := RunTask(func1)
val2 := RunTask(func2)
println(val1.Get() + val2.Get())
}
// повторно используемый хелпер
type Task[Result any] chan Result
func (t Task[Result]) Get() Result {
return <-t
}
func RunTask[Result any](f func() Result) Task[Result] {
res := make(chan Result)
go func() { res <- f() }()
return res
}
S>>·>val1 := func1() // "асинхронный" вызов 1 (на самом деле, конечно же нет) S>>·>val2 := func2() // "асинхронный" вызов 2 (на самом деле, конечно же нет) S>> C# это Task.WhenAll ·>Зачем ты это мне рассказываешь, да ещё и фигню невтемную говоришь? В данном случае будет как-то так: ·>
·>И эти функции ДОЛЖНЫ быть объявлены как async и быть написаны правильно на всю глубину вызовов, протаскивая async повсюду. Сабж! ·>Это же полная Ж по сравнению с java.
Здравствуйте, mrTwister, Вы писали:
T>Делается тривиальный хелпер на три строчки и код будет выглядеть точно так же
И в результате имеем ровно тоже, что и в .net , но там в отличие от GoLang не забыли про ошибки и как и положено сделали каналы опциональными.
Пальцы устали уже писать, что в GoLang нет бесшовной ассинхронности, а вот в Project Loom она таки есть — одну и туже функцию можно запустить и так и этак.