golang сделать обработку многопоточной

Пользователь

zola

от zola , в категории: Вопросы от знатоков , 2 года назад

Есть массивчик и хочу ускорить обработку его и сделать многопоточным как вариант, но не могу понять как это сделать


Вот не многопоточный вариант

1
2
3
4
5
func doubleSlice(s []int) {
    for i := range s {
        s[i] = s[i] * 2
    }
}
Facebook Vk Ok Twitter LinkedIn Telegram Whatsapp Pocket

4 ответа

Пользователь

от monte , 2 года назад

Да многопоточность сильно поможет Вам особенно если массив очень большой

Попробуйте вот так

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func doubleSlice(s []int) {
    var wg sync.WaitGroup
    for i := range s {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            s[i] = s[i] * 2
        }(i)
    }
    wg.Wait()
}

Пользователь

от elena , 2 года назад

А чем RabbitMq не нравится или redis queue ?

Пользователь

от genesis_tillman , 2 года назад
elena: А чем RabbitMq не нравится или redis queue ?

@elena а зачем настраивать и использовать какое то дополнительное обеспечение если нужно для решения одной просто задачи! Они хорошо подходят для решения сложных задач - к примеру Вы посылаете миллионы писем в день! А иногда нужно что то простое!

B

Пользователь

от beyobip796 , месяц назад
monte: Да многопоточность сильно поможет Вам особенно если массив очень большой Попробуйте вот такfunc doubleSlice(s []int) { var wg sync.WaitGroup for i := range s { wg.Add(1) go func(i int) { defer wg.Done() s[i] = s[i] * 2 }(i) } wg.Wait()


тут будет `data race`, ибо пишем в параллельных процессах (горутинах) в одну и туже область памяти (срез `i`)

проверить можно так `go run -race ./main.go`


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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import (
   "fmt"
   "sync"
)

func main() {
   var sliceElementCount int64 = 2048
   // аллокация среза
   s := make([]int64, 0, sliceElementCount)
   // генерирует срез
   for i := int64(1); i <= sliceElementCount; i++ {
      s = append(s, i)
   }
   resultDoubleSlice := doubleSlice(s)
   fmt.Println(resultDoubleSlice)
}

// тип для результат калькуляции
type queueCalcResult struct {
   id  int
   val int64
}

// функция, обсчета среза
func doubleSlice(s []int64) []int64 {
   // определяет длину среза, которую надо обработать
   stackLength := len(s)
   // создаем срез для результатов калькуляции
   result := make([]int64, stackLength)
   // создание waitGroup для синхронизации
   wg := new(sync.WaitGroup)
   // устанавливаем мощность группы ожидания
   wg.Add(stackLength)
   // создаём буферезированный канал, размерностью равной длине входного среза (можно задать иное значение, но какое?)
   queueTaskChannel := make(chan queueCalcResult, stackLength)
   // пробегаемся по срезу
   for i, j := range s {
      // ... порождаю калькуляцию в горутине
      go calc(wg, queueTaskChannel, i, j)
   }
   // ждем, когда отработаю все горутины
   wg.Wait()
   // закрываем канал с результами калькуляции
   close(queueTaskChannel)
   // читаем из канала
   for item := range queueTaskChannel {
      result[item.id] = item.val
   }
   return result
}

// функция калькуляции
func calc(wg *sync.WaitGroup, queue chan<- queueCalcResult, key int, val int64) {
   defer wg.Done()
   // пушим результат калькуляции в канал
   queue <- queueCalcResult{key, val * 2}
}


@monte