2016-08-24 5 views
0

Я новичок в golang. Я пытаюсь выполнить параллельные запросы в mysql db с помощью golang. Я знаю, что каналы могут быть типа интерфейса. Когда я печатаю tableData (type map) в функции RunQuery, я получаю результат. Я посылаю tableData на номер ch, т. Е. Канал типа интерфейса. В функции getdataList Я не получаю никакого значения в ch. Я не понимаю, что я делаю неправильно.канал типа интерфейса не получает значение в golang с MySql

Ниже мой код:

package main 

import (
    "database/sql" 
    "fmt" 
    "net/http" 
    _ "github.com/go-sql-driver/mysql" 
    "log" 
) 


var db *sql.DB 

func getdataList(id int) { 
     ch := make(chan interface{}) 
     done := make (chan bool) 
     RunQuery(ch,"select id,name, last_name,first_name from persons where id= ?", id) 
     go func() { 
      for { 
      x, ok := <-ch //I am not getting any data in channel here 
      if ok { 
       fmt.Println(x) 
      }else { 
       fmt.Println("done") 
       done <- true 
       return 
      } 

     } 
     }() 
    } 

func RunQuery (ch chan interface{}, query string, param interface{}) { 

    stmt, err := db.Prepare(query) 
    if err != nil { 
       panic(err.Error()) 
      } 
    defer stmt.Close() 
    rows, err := stmt.Query(param) 
    columns, err := rows.Columns() 
    if err != nil { 
     fmt.Println("Failed to get columns", err) 
     return 
    } 
    count := len(columns) 
    tableData := make([]map[string]interface{}, 0) 
    values := make([]interface{}, count) 
    valuePtrs := make([]interface{}, count) 
    for rows.Next() { 
     for i := 0; i < count; i++ { 
      valuePtrs[i] = &values[i] 
     } 
     rows.Scan(valuePtrs...) 
     entry := make(map[string]interface{}) 
     for i, col := range columns { 
      var v interface{} 
      val := values[i] 
      b, ok := val.([]byte) 
      if ok { 
       v = string(b) 
      } else { 
       v = val 
      } 
      entry[col] = v 
     } 
     tableData = append(tableData, entry) 
    } 
    fmt.Pritln(tableData) //here I am getting data in map 
    ch <- tableData 
} 


func dbtest(w http.ResponseWriter, req *http.Request) { 

    go getdataList(2) 
    go getdataList(3) 
} 

func main() { 
    var err error 
    db, err = sql.Open("mysql", "root:@/dbName") 
    if err != nil { 
     panic(err.Error()) 
    } 
    defer db.Close() 

    http.HandleFunc("/dbTest", dbtest) 

    log.Fatal(http.ListenAndServe(":8080", nil)) 

} 

ответ

0

Проблема с кодом является то, что он блокирует поток выполнения, прежде чем данные могут быть считаны из канала. Когда вы звоните RunQuery от getdataList, RunQuery пытается отправить данные по каналу ch. Тем не менее, ничего не читается от ch, потому что код для чтения из него находится в getdataList, и это ниже вызова RunQuery.

Таким образом, RunQuery никогда не возвращается, а goroutine для чтения от ch никогда не срабатывает. Для того, чтобы исправить, Вы можете попробовать запустить RunQuery как goroutine, а также:

func getdataList(id int) { 
     ch := make(chan interface{}) 
     done := make (chan bool) 
     // run in a goroutine 
     go RunQuery(ch,"select id,name, last_name,first_name from persons where id= ?", id) 
     go func() { 
      for { 
      x, ok := <-ch //I am not getting any data in channel here 
      if ok { 
       fmt.Println(x) 
      }else { 
       fmt.Println("done") 
       done <- true 
       return 
      } 

     } 
    }() 
} 

Существует еще одна проблема в вашем коде. Вы никогда не закрываете ch. Это может привести к тупиковой ситуации. Самое идеальное место для этого, похоже, находится в RunQuery:

func RunQuery (ch chan interface{}, query string, param interface{}) { 
    // ... 
    ch <- tableData 
    close(ch) 
} 
+0

Спасибо. Его работа прекрасна. У меня есть вопрос, хотя, где я должен добавить close (ch) в свой код. Кроме того, это не печатается. Будет ли он печататься только при закрытии канала? – Jagrati

+0

У меня возникает сомнение, в 'fun dbtest', если я не использую' getdataList' с подпрограммами go. Используя тест apache, я получаю лучшую производительность, если я не использую две функции getdataList с функцией go. Можете ли вы объяснить мне, почему? – Jagrati

+1

Я не могу сказать с уверенностью, но я предполагаю, что с goroutines одновременный доступ заставляет 'db' sql открывать два соединения. Без goroutines, первый вызов 'getdataList' создает одно соединение, которое затем используется вторым вызовом' getdataList'. Моим советом было бы обновить 'getdataList', чтобы он взял список идентификаторов и извлек большую часть данных одним запросом. В этом случае 'getdataList ([] int {2, 3})' с аналогичным обновленным sql-запросом обеспечит еще лучшую производительность. – abhink