आपके पास एक डेडलॉक है। . सबसे खराब स्थिति में आपके पास 15 डेटाबेस कनेक्शन रखने वाले 15 गोरोइन हैं, और उन सभी 15 गोरोइन को जारी रखने के लिए एक नए कनेक्शन की आवश्यकता है। लेकिन एक नया कनेक्शन प्राप्त करने के लिए, एक कनेक्शन को आगे बढ़ाना और जारी करना होगा:गतिरोध।
जुड़ा हुआ विकिपीडिया लेख गतिरोध की रोकथाम का विवरण देता है। उदाहरण के लिए एक कोड निष्पादन केवल एक महत्वपूर्ण खंड (जो संसाधनों को लॉक करता है) में प्रवेश करना चाहिए, जब उसके पास सभी संसाधन हों (या इसकी आवश्यकता होगी)। इस मामले में इसका मतलब है कि आपको 2 कनेक्शन आरक्षित करने होंगे (बिल्कुल 2; यदि केवल 1 उपलब्ध है, तो इसे छोड़ दें और प्रतीक्षा करें), और यदि आपके पास वे 2 हैं, तो ही प्रश्नों के साथ आगे बढ़ें। लेकिन गो में आप पहले से कनेक्शन आरक्षित नहीं कर सकते। जब आप क्वेरी निष्पादित करते हैं तो उन्हें आवश्यकतानुसार आवंटित किया जाता है।
आम तौर पर इस पैटर्न से बचा जाना चाहिए। आपको कोड नहीं लिखना चाहिए जो पहले एक (परिमित) संसाधन (इस मामले में डीबी कनेक्शन) को सुरक्षित रखता है, और इसे जारी करने से पहले, यह एक और मांग करता है।
एक आसान समाधान है पहली क्वेरी को निष्पादित करना, उसके परिणाम को सहेजना (जैसे कि एक गो स्लाइस में), और जब आप इसके साथ काम कर लें, तो बाद के प्रश्नों के साथ आगे बढ़ें (लेकिन sql.Rows
पहला)। इस तरह आपके कोड को एक ही समय में 2 कनेक्शन की आवश्यकता नहीं है।
और त्रुटियों को संभालना न भूलें! मैंने उन्हें संक्षिप्तता के लिए छोड़ दिया, लेकिन आपको अपने कोड में ऐसा नहीं करना चाहिए।
यह इस तरह दिख सकता है:
go func() {
defer wg.Done()
rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
var data []int // Use whatever type describes data you query
for rows.Next() {
var something int
rows.Scan(&something)
data = append(data, something)
}
rows.Close()
for _, v := range data {
// You may use v as a query parameter if needed
db.Exec("SELECT * FROM reviews LIMIT 1")
}
}()
ध्यान दें कि rows.Close()
defer
. के रूप में निष्पादित किया जाना चाहिए यह सुनिश्चित करने के लिए कथन कि इसे निष्पादित किया जाएगा (भयभीत होने की स्थिति में भी)। लेकिन अगर आप बस defer rows.Close()
. का उपयोग करते हैं , जिसे बाद के प्रश्नों के निष्पादित होने के बाद ही निष्पादित किया जाएगा, इसलिए यह गतिरोध को नहीं रोकेगा। तो मैं इसे किसी अन्य फ़ंक्शन (जो एक अनाम फ़ंक्शन हो सकता है) में कॉल करने के लिए इसे दोबारा प्रतिक्रिया दूंगा जिसमें आप defer
का उपयोग कर सकते हैं :
rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
var data []int // Use whatever type describes data you query
func() {
defer rows.Close()
for rows.Next() {
var something int
rows.Scan(&something)
data = append(data, something)
}
}()
यह भी ध्यान दें कि दूसरे for
. में एक तैयार स्टेटमेंट लूप करें (sql.Stmt
) DB.Prepare()
द्वारा अधिग्रहित किया गया
एक ही (पैरामीटरयुक्त) क्वेरी को कई बार निष्पादित करने के लिए शायद एक बेहतर विकल्प होगा।
एक अन्य विकल्प नए गोरोइन में बाद के प्रश्नों को लॉन्च करना है ताकि उसमें निष्पादित क्वेरी तब हो सकती है जब वर्तमान में लॉक कनेक्शन जारी किया जाता है (या किसी अन्य कनेक्शन को किसी अन्य गोरोइन द्वारा लॉक किया जाता है), लेकिन फिर स्पष्ट सिंक्रनाइज़ेशन के बिना आपके पास नियंत्रण नहीं होता है जब वे निष्पादित हो जाते हैं। यह इस तरह दिख सकता है:
go func() {
defer wg.Done()
rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
defer rows.Close()
for rows.Next() {
var something int
rows.Scan(&something)
// Pass something if needed
go db.Exec("SELECT * FROM reviews LIMIT 1")
}
}()
अपने प्रोग्राम को इन गोरआउट्स के लिए भी प्रतीक्षा करने के लिए, WaitGroup
. का उपयोग करें आपके पास पहले से कार्रवाई है:
// Pass something if needed
wg.Add(1)
go func() {
defer wg.Done()
db.Exec("SELECT * FROM reviews LIMIT 1")
}()