2016-10-09 2 views
0

Я не знаком с GO. У меня есть следующий код устаревшего кода.Goroutine ведет себя по-разному в Windows и Linux

var db *sql.DB 

func init() { 
    go feedChan() 

    connString := os.Getenv("DB_CONN") 
    var err error 

    db, err = sql.Open("postgres", connString) 
    if err != nil { 
     log.Fatalf("Failed to connect to database at %q: %q\n", connString, err) 
    } 

    // confirm connection 
    if err = db.Ping(); err != nil { 
     log.Fatalf("Unable to ping database at %q: %q\n", connString, err) 
    } 
} 


func feedChan() { 
    selectQuery, err := db.Prepare(` 
     SELECT id, proxy 
     FROM proxy 
     WHERE fail_count < 2 
     ORDER BY date_added DESC, last_used ASC, fail_count ASC 
     LIMIT 5 
    `) 
    .... 

Следующий код работает над linux. Но это не будет работать на окна с ошибкой ноль на

selectQuery, err := db.Prepare(` 

Какой смысл для меня, так как БД инициализируется после запуска feedChan goroutine. Что для меня не имеет смысла, так это то, почему он работает на Linux.

Итак, вопрос в том, почему этот код работает в Linux без ошибок?

ответ

1

Это, вероятно, race condition. Импорт "time", добавьте эту строку после go feedChan(), и посмотреть, если он все еще работает на Linux:

time.Sleep(3 * time.Second) 

Для того, чтобы избежать этой ситуации, вы можете либо инициализировать dbперед тем вы икру рутину (который использует db) или использовать какие-то barrier:

func init() { 
    barrier := make(chan int) 
    go feedChan(barrier) 

    connString := os.Getenv("DB_CONN") 
    var err error 
    db, err = sql.Open("postgres", connString) 

    if err != nil { 
     log.Fatalf("Failed to connect to database at %q: %q\n", connString, err) 
     // Retry. 
    } else { 
     barrier <- 1 // Opens barrier. 
    } 
    // ... 
} 


func feedChan(barrier chan int) { 
    <-barrier // Blocks until db is ready. 
    selectQuery, err := db.Prepare(` 
     SELECT id, proxy 
     FROM proxy 
     WHERE fail_count < 2 
     ORDER BY date_added DESC, last_used ASC, fail_count ASC 
     LIMIT 5 
    `) 
    // ... 
} 
+0

Да, obv, если инициализировать после его работы. Это не мой вопрос. Я запускаю его много на linux и никогда не получаю эту ошибку. Но как только я запускаю на windows-ноль ошибку (которая исчезает, если перемещение db init после goroutine). Поэтому я хочу понять, почему он никогда не производит эту ошибку в Linux, но 100% на Windows. – Aldarund

+0

Вы пытались спать после 'go feedChan()'? Может быть, несколько секунд, чтобы убедиться, что процедура запускается до 'db' готова. Было бы интересно посмотреть, все ли работает в Linux. – beatngu13

+2

@ Aldarund, как и в любом случае UB, такой вопрос не имеет смысла. Это UB, так что все может случиться. Однажды он может сломаться на Linux, он может работать на нем каждый раз, или ваш компьютер может стать живым и убежать от вас. Это не имеет значения. У вашего кода все еще есть UB. Он имеет два потока выполнения, которые пытаются получить доступ к ресурсу без какой-либо синхронизации - 'func init()' и 'func feedChan()'. В этот момент все может случиться, потому что это состояние гонки между двумя потоками. – creker

0

После прочтения первых строк ваших функций я просто не могу вам сказать, что ваш старый код имеет огромной ошибки и ч n легко фиксируется, просто перемещая эту строку go feedChan() до конца функции init().

Также обратите внимание, что основной причиной является не состояние гонки, просто вопрос ожидания правильной инициализации db переменная.

+0

Два потока обращаются к общему ресурсу («дБ») без синхронизации. Выход программы полностью зависит от времени. Я почти уверен, что это состояние гонки ... – beatngu13

+0

Ну да, есть состояние гонки, но проблема здесь в неинициализированной переменной. Все, что нам нужно сделать, это запустить goroutine 'go feedChan()' после инициализации ** db ** –

+0

Переменная инициализируется на одной машине, но не на другой. Как ты объясняешь это? Если поток, выполняющий 'init()' достаточно быстро, 'db' будет готов _before_, поток, который запускает' feedChan() ', обращается к нему.Если нет, вы получите эту нулевую ошибку. – beatngu13

Смежные вопросы