Pull to refresh
2
0
Eduard @Edison

Software Engineer

Send message
Дело в том, что эту ситуацию описывают как проблему рантайма или планировщика, что не так (даже если планировщик будет уметь приоритеты, все равно может быть все горутины с одинаковым приоритетом).

Конечно же такая ситуация может быть и часто, но еще раз — все потоки выполняются и не заблокированы. Если такая ситуация появилась — нужно либо оптимизировать алгоритм либо скейлить.
Выше было сказано, что код «ставит колом целиком поток», а это не так (все потоки выполняются). Значит неправильно понимают (может я непонятно выразился с «воспринимают»). Но сути дела это не меняет.
Я лишь указал вам на вашу ошибку в восприятии того кода выше (с бесконечным циклом).
У меня?
Еще раз напишу — с точки зрения рантайма все хорошо, все рутины работают и не заблокированы, проблема только в вашем коде.
Смотря как вы будете формировать отчет — вам нужно будет его откуда-то читать, да? Построчно? Вот вам уже и сисколы, уже будет переключение горутин.

Но давайте так, допустим каждая горутина просто должна спать 1 минуту (ну типа чисто в памяти что-то 1 минуту считаем, без сисколов). Сделав 9 запросов, девятый будет ждать. Только это не проблема языка или рантайма, с точки зрения рантайма все очень даже хорошо — все тредэ ранятся и нету заблокированных.
Ну да, тред работает, только остальные горутины остались в пролете. Бесконечный цикл скорее всего исключение, но вот долгое выполнение вполне реально может случиться. Пользователю то пофиг, тред твой тормознулся или занят непонятной херней, ему главное чтобы сайт не тормозил, а вместо этого получит 503.

Вы написали бредовый код и пытаетесь натянуть его на реальную картину — нет, так не работает.
Пишите код, который не будет выполнять непонятную херню.
Если вы заговорили о пользователе, то давайте реальный пример, а не бесконечный цикл.

Сейчас, с точки зрения, что говорил qrKot, у вас ничего не произошло, все потоки ранятся.
Вы же понимаете, что в вашем примере с потоком ничего не произошло?
Да я откуда знаю, может есть еще и другие способы. Тем не менее, это работает не так как написал qrKot. Это не плохо и не хорошо, это примерно также как и у других.


А как? Ваш пример был ошибочным, трэд там в состоянии Running и то что сказал qrKot подтвердилось вашим же примером.

Вы так говорите, как будто это уникальная фича go, да у всех асинхронных серверов так. Понятно что в старых языках типа Java остались синхронные апи для I/O, ну сам себе буратино что используешь их в асинхронном методе. Ну кстати не всегда и требуется переписывать на асинхрон, я как-то переписал консольную утилиту и она стала медленнее работать, накладных расходов потому что стало больше чем полезного кода.


Я не говорю про уникальность, это тут все любят сравнивать, я вам привел пример как сделано в Go. При этом асинхронный I/O это совсем другое (был как пример).
Вы уже уходите от темы, то есть вы согласны что «никакая горутина не может поставить колом целиком поток»?

Конечно же есть затраты на парковку goroutine и context switch (стэк, кстати, у каждой горутины свой и алоцируется при создании), но это ничтожно мало с, например, ожиданием I/O. Как уже говорили, Go хорошо подходит для Web.

Вот в Go, за счет того, что заблокированая горутина паркуется и не блокирует трэд, второй запрос у вас обработается быстрее, так как во время I/O первой горутины, будет выполняться вторая.

В реальном мире да, когда в той же Java на ожидание ответа системного вызова блокируется весь поток.


Чуда не произошло в вашем чудном примере.


UPD: "поставить колом целиком поток" не тоже самое, что поток будет исполняться. Поставить колом — это блокировать, например.

А чего вы хотите увидеть? Я пока вас не понимаю.
В Go кооперативный (почти) планировщик, планирует goroutine по доступным тредам — у вас 8 goroutine не блокируются, не делают системных вызовов, просто инкрементят счетчик, всегда в running состоянии.


Только вот в реально мире такого не будет. Вот добавьте всего одну строчку (fmt.Println(t) в цикле функции test) и у вас совсем другой результат.


func main() {
    for i := 1; i < 100500; i++ {
        go test(0)
    }
    time.Sleep(1)
    go fmt.Println("main 1")
    fmt.Println("main 2")
}

func test(t int) {
    fmt.Println("start")
    for true {
        t = t + 1
        fmt.Println(t)
    }
    fmt.Println("stop")
}

А зачем вы добавили runtime.GOMAXPROCS(1)?

То что в го есть только корпоративная многозадачность

В Go не совсем кооперативный планировщик (https://github.com/golang/go/issues/10958), а еще есть предложение — https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md


Но не думаю что будут менять радикально что-то.

  1. Не знаю что там в C#, говорю как удобно в Go (опять же начались примеры в стиле "а вот в X языке точно такая же фича"). Ну и даже краткость записи — уже ведь лучше, да? Ведь тут все хейтят Go с if err != nil. В вашем примере мне не понятно, что такое Task, это ваш класс или со стандартной библиотеки?


  2. Можно сделать все что угодно, только вот в Go это часть стандартной библиотеки (как и пакеты context и sync для управления goroutine).



В сумме все это очень упрощает написания конкурентного кода.

Связка tokio+futures-await так же удобны в использование как горутины.

Прям вот точно также удобны?
Удобство goroutine в том, что их просто стартануть (go перед любой функцией/методом) и то, что есть каналы для их взаимодействия. В Rust тоже есть каналы?

Я уже почти 4 года программирую на Go: мне не нужно генерировать шаблонный, мне не нужны дженерики, уже давно есть удобный пакетный менеджер (сейчас еще модули завезли), компилятор очень сильно помогает (зависит с чем сравнивать, да?). В основном я пишу/писал web сервисы (нагрузка от 30к до 400к RPS), стрим процессинг с Kafka, тулзы для Kubernetes, etc.
Мне нравится Go, мне нравится вот это if err != nil, после Java я совсем по другому начал относится с ошибкам.


И я совсем не понимаю, почему пользователи Хабра в каждом посте про Go начинают бегать туда-сюда с криками насколько Go ужасный язык, при этом 80% из них на Go не программируют. Начинают сравнивать отдельные фичи Go с фичами других языков — а вот в Х яызке это лучше, а вот в Y языке другое лучше:
— Но я использую Go, мне нравится X фича и для моих задач Go подходит идеально. Тем более у нас есть опыт на Go.
— Ты не понял, Go говно, там if err != nil, вот тебе Rust/C++/Haskell/JavaScript там еще лучше. Монад ведь нету в Go? Воот, ерунда ваш Go.


В каждом посте овер 200 комментов, больше половины из которых вот такие бессмысленные срачи.

Да согласен.
Я все время ссылался на эту доку, про то, что вы писали так и не нашел.
То, о чем я говорю
fn call<F>(a: i64, _b: i64, c: F) -> i64
where
    F: Fn(i64) -> i64,
{
    c(a)
}

fn main() {
    let example_fn = |a| a;
    println!("{}", call(10, 10, example_fn));

    println!("{}", example_fn(String::from("hello"))); // ошибка
}


Тип example_fn определился при первом вызове.
В примере выше вывод типа происходит у аргумента замыкания. В примере с функцией call, так же вывод типа происходит у аргумента замыкания за счёт того, что в сигнатуре функции явно указан тип аргумента замыкания.

Можете дать ссылку на документацию?


Потому что в примере:


let example_fn: &Fn(String) -> String = &|a| a;
println!("{}", example_fn("Hello, world!".into()));

Вы же указали тип функции и тип аргумента функции, вот же он &Fn(String). Функция example_fn ждет первым аргументом String. Если там не String — то ошибка компиляции.


Ваш изначальный пример — это свойства анонимных функций, а не вывод типа анонимной функции из сигнатуры.

Information

Rating
Does not participate
Location
Киев, Киевская обл., Украина
Registered
Activity