Составьте сделанные каналы в один канал [дубликат]

О некоторых частях ваших вопросов (из года в год):

1.Если вы имеете в виду, что значение по умолчанию было бы настолько умным, и вам не понадобилось бы его менять в будущем, тогда ваш желание ошибочно. почему?

по двум причинам:

  • a) В концепции значения по умолчанию (во всех языках программирования, в DB и т. д. ..) значение по умолчанию не является чем-то, что динамически заменяет значение тем, что вы хотите. Например, Func Sum (a, b, c = 10), в этой ситуации, если вы не введете параметр c, это займет 10, иначе вы должны что-то ввести. поэтому значения по умолчанию являются предсказуемыми и расчетными значениями, а не интеллектуальными значениями.
  • b) Внешние ключи являются чем-то еще более чувствительным, чем необязательный параметр в методе, из-за реляционных значений в RDBMS, так что, конечно, вы должны отредактировать это в будущем, даже иногда это может быть связано с использованием DB.

2. Он может обрабатывать код, который я вам покажу.

Поэтому, основываясь на этих объяснениях, у вас может быть столбец со значением по умолчанию, который существует в Fkeys, но это не будет чем-то, что вам нужно, и вы должны обновить его в будущей базе при использовании. и для этого вам нужно:

FIRST:

  • Создайте функцию, которая возвращает действительный и существующий внешний ключ определенной таблицы следующим образом:
  CREATE FUNCTION SelectMinForeignKey () RETURNS INT AS BEGIN DECLARE @FirstID INT SELECT @FirstID = MIN (ID) из DetailTableExample01 RETURN @FirstID END  

SECOND:

  • Затем вы должны изменить таблицу, чтобы добавить столбец следующим образом:
  ALTER TABLE example1 ADD NoNullableCol INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey ()  
  • или с добавлением Relation мгновенно:
  ALTER TABLE example1 ADD NoNullableCol2 INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey (), FOREIGN  KEY (NoNullableCol2) ССЫЛКИ DetailTableExample01 (id);   
  • или более в комплекте с добавлением ограничения мгновенно и назначаемым именем FK:
  ALTER TABLE dbo.example1 ADD NoNullableCol INT NOT NULL  DEFAULT [dbo] .SelectMinForeignKey (), CONSTRAINT FK_example1_DetailTableExample01 ИНОСТРАННЫЙ КЛЮЧ (NoNullableCol) СПИСОК ЛИТЕРАТУРЫ dbo.DetailTableExample01 (ID) В ОБНОВЛЕНИИ CASCADE ON DELETE CASCADE;   
  • или:
  ALTER TABLE dbo.example1 ADD NoNullableCol INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey () GO ALTER  ТАБЛИЦА dbo.example1 ADD CONSTRAINT FK_example1_DetailTableExample01 ИНОСТРАННЫЙ КЛЮЧ (NoNullableCol) ССЫЛКИ dbo.DetailTableExample01 (ID) В ОБНОВЛЕНИИ CASCADE ON DELETE CASCADE  

ПРИМЕЧАНИЕ. Как вы знаете, имена таблиц и столбцов являются образцами.

THIRD:

  • Теперь вы должны изменить значения в NoNullableCol, как вы хотите

Все-в-одном:

  • Весь запрос будет выглядеть примерно так:
  CREATE FUNCTION SelectMinForeignKey () RETURNS INT AS BEGIN DECLARE @FirstID INT SELECT @FirstID = MIN (ID) из  DetailTableExample01 RETURN @FirstID END GO ALTER TABLE dbo.example1 ADD NoNullableCol INT NOT NULL DEFAULT [dbo] .SelectMinForeignKey (), CONSTRAINT FK_example1_DetailTableExample01 ИНОСТРАННЫЙ КЛЮЧ (NoNullableCol) ССЫЛКИ dbo.DetailTableExample01 (ID) ВКЛ. ОБНОВЛЕНИЕ КАСКАДЫ НА УДАЛИТЬ КАСКАД;   

И все готово!

Надеюсь, он решает вашу проблему

85
задан JohnSmith 15 November 2013 в 04:18
поделиться

4 ответа

Вы можете сделать это, используя функцию Select из пакета reflect :

func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)

Select выполняет операцию выбора описанных в списке случаев. Подобно оператору выбора Go, он блокируется, пока, по крайней мере, один из случаев не может продолжить, делает равномерный псевдослучайный выбор, а затем выполняет этот случай. Он возвращает индекс выбранного случая и, если в этом случае была операция приема, полученное значение и логическое значение указывают, соответствует ли значение отправке по каналу (в отличие от нулевого значения, полученного, поскольку канал закрыт).

Вы передаете массив структур SelectCase, которые определяют канал для выбора, направление операции и значение для отправки в случае операции отправки.

Итак, вы можете сделать что-то вроде этого:

cases := make([]reflect.SelectCase, len(chans))
for i, ch := range chans {
    cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
}
chosen, value, ok := reflect.Select(cases)
# ok will be true if the channel has not been closed.
ch := chans[chosen]
msg := value.String()

Вы можете поэкспериментировать с более сложным примером здесь: http://play.golang.org/p/8zwvSk4kjx

104
ответ дан Flimzy 16 August 2018 в 03:32
поделиться
  • 1
    Существует ли практическое ограничение количества случаев в таком выборе? Тот, который, если вы выходите за пределы этого, то производительность сильно влияет? – Maxim Vladimirsky 19 October 2015 в 23:39
  • 2
    Возможно, это моя некомпетентность, но я обнаружил, что этот шаблон очень трудно работать, когда вы отправляете & amp; получая сложные структуры через канал. Передача общего "совокупного" канал, как сказал Тим Allclair, было намного проще в моем случае. – boramalper 20 July 2017 в 08:13
  • 3
    это классно круто – Alexander Guo 9 September 2017 в 02:15

Чтобы расширить некоторые комментарии к предыдущим ответам и дать более четкое сравнение, приведен пример обоих подходов, представленных до сих пор, с учетом того же ввода, фрагмента каналов для чтения и функции для вызова каждого значения, которое также необходимо чтобы узнать, к какому каналу пришло значение.

Существуют три основных различия между подходами:

  • Сложность. Хотя это может быть частично предпочтительным для читателя, я нахожу подход канала более идиоматичным, прямолинейным и удобочитаемым.
  • Производительность. В моей системе Xeon amd64 каналы goroutines + channels выполняют решение отражения примерно на два порядка (в общем отражение в Go часто медленнее и должно использоваться только тогда, когда это абсолютно необходимо). Конечно, если есть какая-либо значительная задержка либо в функции обработки результатов, либо при записи значений на входные каналы, это различие в производительности может стать незначительным.
  • Семантика блокировки / буферизации. Важность этого зависит от варианта использования. Чаще всего это либо не имеет значения, либо небольшая дополнительная буферизация в решении слияния goroutine может быть полезна для пропускной способности. Однако, если желательно иметь семантику, что только один писатель разблокирован, и это значение полностью обрабатывается до , любой другой писатель разблокирован, то это может быть достигнуто только с помощью решения отражения.

Обратите внимание, что оба подхода могут быть упрощены, если «id» передающего канала не требуется или если исходные каналы никогда не будут закрыты.

Канал объединения Goroutine:

// Process1 calls `fn` for each value received from any of the `chans`
// channels. The arguments to `fn` are the index of the channel the
// value came from and the string value. Process1 returns once all the
// channels are closed.
func Process1(chans []<-chan string, fn func(int, string)) {
    // Setup
    type item struct {
        int    // index of which channel this came from
        string // the actual string item
    }
    merged := make(chan item)
    var wg sync.WaitGroup
    wg.Add(len(chans))
    for i, c := range chans {
        go func(i int, c <-chan string) {
            // Reads and buffers a single item from `c` before
            // we even know if we can write to `merged`.
            //
            // Go doesn't provide a way to do something like:
            //     merged <- (<-c)
            // atomically, where we delay the read from `c`
            // until we can write to `merged`. The read from
            // `c` will always happen first (blocking as
            // required) and then we block on `merged` (with
            // either the above or the below syntax making
            // no difference).
            for s := range c {
                merged <- item{i, s}
            }
            // If/when this input channel is closed we just stop
            // writing to the merged channel and via the WaitGroup
            // let it be known there is one fewer channel active.
            wg.Done()
        }(i, c)
    }
    // One extra goroutine to watch for all the merging goroutines to
    // be finished and then close the merged channel.
    go func() {
        wg.Wait()
        close(merged)
    }()

    // "select-like" loop
    for i := range merged {
        // Process each value
        fn(i.int, i.string)
    }
}

Выбор выделения:

// Process2 is identical to Process1 except that it uses the reflect
// package to select and read from the input channels which guarantees
// there is only one value "in-flight" (i.e. when `fn` is called only
// a single send on a single channel will have succeeded, the rest will
// be blocked). It is approximately two orders of magnitude slower than
// Process1 (which is still insignificant if their is a significant
// delay between incoming values or if `fn` runs for a significant
// time).
func Process2(chans []<-chan string, fn func(int, string)) {
    // Setup
    cases := make([]reflect.SelectCase, len(chans))
    // `ids` maps the index within cases to the original `chans` index.
    ids := make([]int, len(chans))
    for i, c := range chans {
        cases[i] = reflect.SelectCase{
            Dir:  reflect.SelectRecv,
            Chan: reflect.ValueOf(c),
        }
        ids[i] = i
    }

    // Select loop
    for len(cases) > 0 {
        // A difference here from the merging goroutines is
        // that `v` is the only value "in-flight" that any of
        // the workers have sent. All other workers are blocked
        // trying to send the single value they have calculated
        // where-as the goroutine version reads/buffers a single
        // extra value from each worker.
        i, v, ok := reflect.Select(cases)
        if !ok {
            // Channel cases[i] has been closed, remove it
            // from our slice of cases and update our ids
            // mapping as well.
            cases = append(cases[:i], cases[i+1:]...)
            ids = append(ids[:i], ids[i+1:]...)
            continue
        }

        // Process each value
        fn(ids[i], v.String())
    }
}

[Полный код на игровой площадке Go .]

14
ответ дан Dave C 16 August 2018 в 03:32
поделиться
  • 1
    Также стоит отметить, что решение goroutines + channels не может делать все select или reflect.Select. Горотины будут продолжать вращаться, пока они не будут потреблять все из каналов, поэтому нет четкого пути, по которому вы могли бы Process1 выйти раньше. Существует также проблема для проблем, если у вас несколько читателей, поскольку goroutines задерживают один элемент из каждого из каналов, чего не будет с select. – James Henstridge 4 November 2015 в 04:44
  • 2
    @JamesHenstridge, ваша первая заметка об остановке не соответствует действительности. Вы бы остановили Process1 точно так же, как вы бы остановили Process2; например путем добавления "остановки" канал, который закрыт, когда горуты должны остановиться. Process1 понадобится два случая select в цикле for вместо более простого for range в настоящее время. Process2 нужно было бы вставить еще один случай в cases и специальный дескриптор этого значения i. – Dave C 7 January 2016 в 21:42
  • 3
    Это все еще не решает проблему, что вы читаете значения из каналов, которые не будут использоваться в раннем случае остановки. – James Henstridge 10 January 2016 в 02:16

Почему этот подход не работает, если кто-то отправляет события?

func main() {
    numChans := 2
    var chans = []chan string{}

    for i := 0; i < numChans; i++ {
        tmp := make(chan string)
        chans = append(chans, tmp)
    }

    for true {
        for i, c := range chans {
            select {
            case x = <-c:
                fmt.Printf("received %d \n", i)
                go DoShit(x, i)
            default: continue
            }
        }
    }
}
1
ответ дан noonex 16 August 2018 в 03:32
поделиться
  • 1
    Это спиновая петля. Ожидая ввода входного канала, это потребляет весь доступный CPU. Вся точка select на нескольких каналах (без предложения default) заключается в том, что она эффективно ждет, пока, по крайней мере, один не будет готов к вращению. – Dave C 29 June 2018 в 14:18

Вы можете выполнить это, обернув каждый канал в goroutine, который «пересылает» сообщения на общий «совокупный» канал. Например:

agg := make(chan string)
for _, ch := range chans {
  go func(c chan string) {
    for msg := range c {
      agg <- msg
    }
  }(ch)
}

select {
case msg <- agg:
    fmt.Println("received ", msg)
}

Если вам нужно знать, с какого канала отправлено сообщение, вы можете перенести его в структуру с какой-либо дополнительной информацией, прежде чем перенаправлять ее на общий канал.

В моем (ограниченном) тестировании этот метод значительно отличается от используемого пакета отражения:

$ go test dynamic_select_test.go -test.bench=.
...
BenchmarkReflectSelect         1    5265109013 ns/op
BenchmarkGoSelect             20      81911344 ns/op
ok      command-line-arguments  9.463s

Код контрольной точки здесь

49
ответ дан Tim Allclair 16 August 2018 в 03:32
поделиться
  • 1
    Ваш тестовый код неверен, вам нужно перебирать b.N в рамках теста. В противном случае результаты (которые делятся на b.N, 1 и 2000000000 в вашем выходе) будут совершенно бессмысленными. – Dave C 2 September 2015 в 16:27
  • 2
    @DaveC Спасибо! Вывод не меняется, но результаты гораздо более разумны. – Tim Allclair 2 September 2015 в 17:09
  • 3
    Действительно, я быстро взломал ваш тестовый код, чтобы получить некоторые фактические числа . Там очень хорошо может быть что-то, что все еще отсутствует / неверно из этого теста, но единственное, что делает более сложный код отражения, заключается в том, что настройка выполняется быстрее (с GOMAXPROCS = 1), так как ему не нужна куча goroutines. В каждом другом случае простой канал слияния goroutine сдувает решение отражения (на ~ 2 порядка). – Dave C 2 September 2015 в 17:28
  • 4
    Одним из важных недостатков (по сравнению с подходом reflect.Select) является то, что goroutines выполняют слияние буфера как минимум на одном значении на каждом канале, который объединяется. Обычно это не будет проблемой, но в некоторых конкретных приложениях, которые могут быть прерывателем сделки :(. – Dave C 2 September 2015 в 17:36
  • 5
    буферный канал слияния затрудняет проблему. Проблема в том, что только решение отражения может иметь полностью небуферизированную семантику. Я пошел вперед и разместил тестовый код, который я экспериментировал, в качестве отдельного ответа (надеюсь) пояснить, что я пытался сказать. – Dave C 3 September 2015 в 16:54
Другие вопросы по тегам:

Похожие вопросы: