[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blogs-th-1-1-all-golang-the-series-ep161-async-ai-tasks-worker-pools-all--*":3,"academy-blog-translations-9ml9zd4i9t06yo5":94},{"data":4,"page":80,"perPage":80,"totalItems":80,"totalPages":80},[5],{"alt":6,"collectionId":7,"collectionName":8,"content":9,"cover_image":10,"cover_image_path":11,"created":12,"created_by":13,"expand":14,"id":88,"keywords":89,"locale":60,"published_at":90,"scheduled_at":76,"school_blog":84,"short_description":91,"status":82,"title":92,"updated":93,"updated_by":13,"slug":85,"views":87},"ภาพหน้าปกบทความ Golang The Series EP.161 แสดงหัวข้อ Async AI Tasks การใช้ Worker Pools จัดการคิวงาน AI พร้อมภาพประกอบหน้าจอโค้ดโปรแกรมภาษา Go","sclblg987654321","school_blog_translations","\u003Cp>ยินดีต้อนรับเข้าสู่ EP.161 ครับ! หลังจากที่เราได้ร่วมกันสร้าง Internal AI Tool แยกเลเยอร์อย่างเป็นระบบไปในตอนที่แล้ว เมื่อคลังข้อมูลเริ่มใหญ่ขึ้นและมีพนักงานเข้ามาใช้งานพร้อมกัน สาย Backend จะต้องเจอกับความท้าทายใหม่ นั่นคือ \u003Cstrong>\"ปัญหาคอขวดเมื่อมีงานประมวลผลขนาดใหญ่เข้ามาพร้อมกัน\"\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>ลองนึกภาพว่าถ้ามีพนักงานอัปโหลดไฟล์ PDF หนา 500 หน้าเข้ามาให้ AI สรุป หรือสั่งรัน Ingestion Pipeline แปลงเอกสารนับร้อยไฟล์พร้อมๆ กัน การประมวลผลผ่าน HTTP Request ตรงๆ (Synchronous) จะทำให้ผู้ใช้ต้องนั่งรอจนหน้าเว็บเกิด Timeout แน่นอน แถมยังเสี่ยงทำเซิร์ฟเวอร์ล่มจากการกิน CPU และ Memory จนทะลุขีดจำกัด\u003C\u002Fp>\u003Cp>ทางออกที่ดีที่สุดในสถาปัตยกรรมของ Go คือการเปลี่ยนงานเหล่านั้นให้เป็น \u003Cstrong>Async Tasks (งานประมวลผลเบื้องหลัง)\u003C\u002Fstrong> และควบคุมปริมาณการทำงานขนานด้วย \u003Cstrong>Worker Pools Pattern\u003C\u002Fstrong> ครับ!\u003C\u002Fp>\u003Ch2>แนวคิด Worker Pools ในการควบคุมทรัพยากร AI\u003C\u002Fh2>\u003Cp>\u003Cstrong>Worker Pools\u003C\u002Fstrong> คือการสถาปนา Goroutines ขึ้นมาจำนวนหนึ่งที่คงที่ในระบบ (เช่น กำหนดให้มีพนักงานคอยประมวลผล 3 ตัว) เพื่อให้คอย \"ดึงงาน\" ออกมาจากคิวกลาง (\u003Cstrong>Go Channel\u003C\u002Fstrong>) ไปทำทีละงานแบบ\u003Cstrong>อซิงโครนัส (Asynchronous)\u003C\u002Fstrong> ซึ่งช่วยแก้ปัญหาหลักๆ ได้ดังนี้ครับ:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>ป้องกัน Boundless Concurrency:\u003C\u002Fstrong> ช่วยควบคุมไม่ให้ระบบสร้าง Goroutine ออกมาใหม่แบบไร้ขีดจำกัดตามจำนวน Request ซึ่งเป็นสาเหตุหลักที่ทำให้ Memory เต็ม (OOM - Out of Memory)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>Rate Limiting ในตัว:\u003C\u002Fstrong> ช่วยให้เราควบคุมอัตราการยิงไปหา OpenAI, Claude หรือ Local Ollama ไม่ให้เกินโควตาจราจร (Rate Limit) ของ API Key หรือเกินขีดจำกัดพละกำลังของฮาร์ดแวร์เราด้วยครับ\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>ดีไซน์ระบบโครงสร้างคิวงาน (Job Queue Structural Design)\u003C\u002Fh2>\u003Cp>เพื่อให้โค้ดอ่านง่ายและนำไปขยายต่อในโปรเจกต์จริงได้ทันที เราจะแบ่งประเภทข้อมูลออกเป็น 2 ส่วนหลัก คือ \u003Cstrong>Job\u003C\u002Fstrong> (ตัวแทนชิ้นงานที่วิ่งเข้าคิว) และ \u003Cstrong>Result\u003C\u002Fstrong> (ผลลัพธ์ที่ได้หลังจากพนักงานประมวลผลเสร็จ) ดังนี้ครับ:\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package main\n\nimport (\n\t\"context\"\n)\n\n\u002F\u002F Job แทนโครงสร้างงาน AI ที่เราต้องการทำเบื้องหลัง\ntype Job struct {\n\tID       int\n\tFilePath string\n\tCtx      context.Context \u002F\u002F พ่วง Context สำหรับควบคุม Timeout ของแต่ละชิ้นงาน\n}\n\n\u002F\u002F Result แทนผลลัพธ์ที่ได้จากการประมวลผลเสร็จสิ้น\ntype Result struct {\n\tJobID int\n\tData  string\n\tError error\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>ตัวอย่างการเขียน Go สร้าง Worker Pools จัดการคิวงาน AI\u003C\u002Fh2>\u003Cp>มาดูตัวอย่างการเขียนโค้ดเพื่อควบคุมการทำงานแบบ Async โดยจำลองการดึงไฟล์ PDF ในคิวไปประมวลผลสกัดข้อความและทำ Embedding ขนานกันครับ\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\n\u002F\u002F worker ทำหน้าที่เสมือนพนักงานเบื้องหลังที่คอยดึงงานจากช่อง jobs ไปทำ\nfunc worker(id int, jobs &lt;-chan Job, results chan&lt;- Result, wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\t\u002F\u002F วนลูปดึงงานจาก Channel ไปทำเรื่อยๆ จนกว่า Channel จะถูกปิด\n\tfor job := range jobs {\n\t\tfmt.Printf(\"👷 [Worker %d] เริ่มประมวลผลงานชิ้นที่ %d: %s\\n\", id, job.ID, job.FilePath)\n\t\t\n\t\t\u002F\u002F จำลองกระบวนการที่ใช้เวลาสูง เช่น สกัดข้อความ PDF หรือยิงไปแปลงเวกเตอร์\n\t\terr := processAITask(job.Ctx, job.FilePath)\n\t\t\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"❌ [Worker %d] งานชิ้นที่ %d ล้มเหลว: %v\\n\", id, job.ID, err)\n\t\t\tresults &lt;- Result{JobID: job.ID, Error: err}\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\tfmt.Printf(\"✅ [Worker %d] ทำงานชิ้นที่ %d สำเร็จ!\\n\", id, job.ID)\n\t\tresults &lt;- Result{\n\t\t\tJobID: job.ID, \n\t\t\tData:  fmt.Sprintf(\"สกัดข้อความสำเร็จจากไฟล์ %s\", job.FilePath), \n\t\t\tError: nil,\n\t\t}\n\t}\n}\n\n\u002F\u002F ฟังก์ชันจำลองการทำงานประมวลผล AI\nfunc processAITask(ctx context.Context, path string) error {\n\tselect {\n\tcase &lt;-time.After(2 * time.Second): \u002F\u002F จำลองการทำงาน 2 วินาที\n\t\treturn nil\n\tcase &lt;-ctx.Done(): \u002F\u002F ถ้าระบบหลักสั่ง Cancel หรือ Timeout ก่อน ให้เลิกทำทันที\n\t\treturn ctx.Err()\n\t}\n}\n\nfunc main() {\n\tnumJobs := 10\n\tnumWorkers := 3 \u002F\u002F โควตาพนักงาน: ควบคุมให้รันพร้อมกันสูงสุดแค่ 3 งานเพื่อประหยัด RAM\u002FCPU\n\n\tjobs := make(chan Job, numJobs)\n\tresults := make(chan Result, numJobs)\n\n\tvar wg sync.WaitGroup\n\tctx := context.Background()\n\n\t\u002F\u002F 1. สตาร์ท Workers ตามจำนวนที่กำหนดไว้ (Fixed Pool Size)\n\tfor w := 1; w &lt;= numWorkers; w++ {\n\t\twg.Add(1)\n\t\tgo worker(w, jobs, results, &amp;wg)\n\t}\n\n\t\u002F\u002F 2. โยนงาน 10 ชิ้น เข้าสู่ท่อ Queue (จำลองสถานการณ์พนักงานรุมอัปโหลดไฟล์พร้อมกัน)\n\tfor j := 1; j &lt;= numJobs; j++ {\n\t\tjobCtx, _ := context.WithTimeout(ctx, 5*time.Second) \u002F\u002F แต่ละงานมีเวลารันไม่เกิน 5 วินาที\n\t\tjobs &lt;- Job{\n\t\t\tID:       j, \n\t\t\tFilePath: fmt.Sprintf(\"company_policy_part_%d.pdf\", j),\n\t\t\tCtx:      jobCtx,\n\t\t}\n\t}\n\tclose(jobs) \u002F\u002F สำคัญมาก: ปิดช่องรับงานเพื่อบอก Worker ว่าไม่มีงานใหม่เพิ่มแล้ว ลูป for range จะได้สิ้นสุด\n\n\t\u002F\u002F 3. รอให้พนักงานทุกตัวเคลียร์งานจนเสร็จสิ้นทั้งหมด\n\twg.Wait()\n\tclose(results) \u002F\u002F ปิดช่องรับผลลัพธ์\n\n\t\u002F\u002F 4. ดึงผลลัพธ์ของคิวงานทั้งหมดมาสรุปรายงาน\n\tfmt.Println(\"\\n🏁 งานในคิวประมวลผลเสร็จสิ้นทั้งหมดเรียบร้อย!\")\n\tfor res := range results {\n\t\tif res.Error != nil {\n\t\t\tfmt.Printf(\"⚠️ รายงานผล: งานชิ้นที่ %d เกิดข้อผิดพลาด: %v\\n\", res.JobID, res.Error)\n\t\t} else {\n\t\t\tfmt.Printf(\"ℹ️ รายงานผล: งานชิ้นที่ %d -&gt; %s\\n\", res.JobID, res.Data)\n\t\t}\n\t}\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>การต่อยอดสู่ระบบ Production ขนาดใหญ่\u003C\u002Fh2>\u003Cp>เมื่อระบบของคุณเติบโตไปสู่ระดับ Enterprise ที่มีผู้ใช้งานพร้อมกันหลายพันคน สิ่งที่ต้องคำนึงถึงมีดังนี้ครับ:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>In-Memory to Distributed Queues:\u003C\u002Fstrong> Go Channels ทำงานได้ดีมากในหน่วยความจำ (In-Memory) ของเซิร์ฟเวอร์เครื่องเดียว แต่ถ้าคุณต้องการขยายระบบ (Scaling) ไปเป็นแบบหลายเครื่อง (Microservices) แนะนำให้เปลี่ยนจาก Channel ไปใช้คิวภายนอกอย่าง \u003Cstrong>RabbitMQ\u003C\u002Fstrong> หรือ \u003Cstrong>Redis\u003C\u002Fstrong> (แนะนำไลบรารีชื่อ \u003Cstrong>Asynq\u003C\u002Fstrong> ของ Go) อย่างไรก็ตาม โครงสร้างดีไซน์ภายในตัว Worker ใน Go Application ก็ยังคงใช้แนวคิด Worker Pools นี้ในการจัดการอยู่ดีครับ\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>Context Propagation:\u003C\u002Fstrong> การส่ง \u003Ccode>context.Context\u003C\u002Fcode> พ่วงเข้าไปใน \u003Ccode>Job\u003C\u002Fcode> (ตามตัวอย่างโค้ดด้านบน) เป็นท่ามาตรฐานที่ขาดไม่ได้ เพราะช่วยให้เราสามารถส่งต่อสัญญาณเพื่อยกเลิกการทำงาน (Abort) ฝั่ง API ได้ทันที หากระบบเครือข่ายมีปัญหา หรือโมเดล LLM เจ้านั้นค้างเป็นเวลานานเกินไป\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>🎯 ท้าให้ลอง (Daily Mission)\u003C\u002Fh2>\u003Cp>ลองนำโครงโค้ด Worker Pools ตัวนี้ไปรันในเครื่องของคุณดูครับ แล้วลองปรับแต่งตัวแปร \u003Ccode>numWorkers\u003C\u002Fcode> จาก 3 ตัว ลดเหลือ 1 ตัว หรือเพิ่มเป็น 5 ตัวดูครับ\u003C\u002Fp>\u003Cp>\u003Cstrong>การบ้านชวนคิด:\u003C\u002Fstrong> สังเกตความเร็วในการพ่นข้อความบน Terminal ดูครับว่า ระยะเวลาในการเคลียร์งานทั้งหมด 10 ชิ้นเปลี่ยนไปอย่างไร?\u003C\u002Fp>\u003Cp>และโจทย์สำคัญ: \u003Cem>หากคุณต้องการบันทึกสถานะความคืบหน้าของงาน (Job Status เช่น Pending -&gt; Processing -&gt; Success) ลงฐานข้อมูล เพื่อให้ฝั่งหน้าบ้าน (Frontend) สามารถยิง API มาดึงเปอร์เซ็นต์ Progress ไปแสดงผลบน UI ได้ คุณคิดว่าควรวางฟังก์ชันสำหรับอัปเดตสถานะฐานข้อมูลไว้ที่ส่วนใดในโครงสร้างโค้ดนี้?\u003C\u002Fem> ลองดีไซน์สถาปัตยกรรมในหัวกันดูนะครับ!\u003C\u002Fp>\u003Ch2>FAQ: คำถามที่พบบ่อยเกี่ยวกับ Worker Pools ในงาน AI\u003C\u002Fh2>\u003Ch3>เราควรตั้งจำนวน Worker (\u003Ccode>numWorkers\u003C\u002Fcode>) เท่าไหร่ดีที่สุด?\u003C\u002Fh3>\u003Cp>ไม่มีตัวเลขตายตัวครับ ขึ้นอยู่กับข้อจำกัดของระบบ หากเป็นงานที่กิน CPU\u002FMemory สูง เช่น การรันโมเดล AI ในเครื่องตัวเอง (Local LLM) ควรตั้งจำนวนตามจำนวน Core ของ CPU แต่หากเป็นงานประเภท Network I\u002FO เช่น การยิง API ไปหา OpenAI หรือ Claude จำนวน Worker จะขึ้นอยู่กับข้อจำกัด Rate Limit ของ API Key และพละกำลังของเซิร์ฟเวอร์เราในการรองรับการรันขนานครับ แนะนำให้เริ่มจากตัวเลขน้อยๆ (เช่น 3-5 ตัว) แล้วทำ Load Test เพื่อดูจุดที่เหมาะสมที่สุด\u003C\u002Fp>\u003Ch3>ถ้ามีงานในคิวมากเกินไปจนท่อ Channel เต็ม จะส่งผลอย่างไร?\u003C\u002Fh3>\u003Cp>หากไม่ได้กำหนดขนาดของ Channel ไว้ (Unbuffered Channel) หรือกำหนดไว้แต่เต็มแล้ว ตัว Go Application ฝั่งที่โยนงานเข้าคิว (Publisher) จะเกิดการ Block (หยุดรอ) ตรงบรรทัดที่ส่งงานเข้า Channel ทันที ซึ่งในระบบ Production จริง เรามักจะแก้ปัญหานี้ด้วยการใช้ Non-blocking select หรือเปลี่ยนไปใช้ Distributed Queue ภายนอกแทน\u003C\u002Fp>\u003Cdiv data-type=\"horizontalRule\">\u003Chr>\u003C\u002Fdiv>\u003Ch2>สรุป\u003C\u002Fh2>\u003Cp>ในบทความนี้เราได้เรียนรู้เรื่องการนำสถาปัตยกรรม \u003Cstrong>Worker Pools Pattern\u003C\u002Fstrong> มาประยุกต์ใช้ใน Go (Golang) เพื่อจัดการกับคิวงานประมวลผล AI แบบอซิงโครนัส (Asynchronous Tasks) ซึ่งช่วยแก้ปัญหาคอขวดบนเซิร์ฟเวอร์ได้อย่างมีประสิทธิภาพ ช่วยจำกัดการใช้ทรัพยากรไม่ให้เกิดปัญหา Memory เต็ม (OOM) และยังมีกลไก Rate Limiting ในตัวเพื่อควบคุมการเรียกใช้ AI API อีกด้วย การออกแบบโครงสร้างโค้ดด้วยรูปแบบนี้จะช่วยให้ระบบฝั่ง Backend ของเรามีความเสถียรและพร้อมรองรับการขยายตัวในระดับ Enterprise ได้เป็นอย่างดีครับ\u003C\u002Fp>\u003Cp>\u003Cstrong>ในตอนต่อไป (EP.162):\u003C\u002Fstrong> เมื่อเราจัดการจัดคิวงานเบื้องหลังได้อย่างอยู่หมัดแล้ว ปัญหาถัดมาของระบบ AI ก็คือ \u003Cem>\"เราจะเลือกใช้โมเดล AI เจ้าไหนดี?\"\u003C\u002Fem> บางเจ้าวิเคราะห์สถิติเก่ง บางเจ้าแปลภาษาหรือสรุปข้อความเก่งกว่า ตอนหน้าเราจะพาไปปลดล็อกขีดจำกัดด้วยเรื่อง \u003Cstrong>\"Goroutines for Multi-LLM — เรียก AI หลายเจ้าพร้อมกันเพื่อเปรียบเทียบผลลัพธ์แบบ Parallel\"\u003C\u002Fstrong> มารุมถาม AI พร้อมกันขนานกันเถอะ gophers\u003C\u002Fp>\u003Cp>\u003Cstrong>ฝากกดติดตามพวกเราได้ที่ Superdev Academy\u003C\u002Fstrong> ในทุกช่องทางนะครับ!\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>🔵 Facebook: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.facebook.com\u002Fsuperdev.academy.th\">\u003Cstrong>Superdev Academy Thailand\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(อัปเดตข่าวสารและบทความใหม่)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 YouTube: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.youtube.com\u002F@SuperdevAcademy\">\u003Cstrong>Superdev Academy Channel\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(ติวเข้มแบบวิดีโอ)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>📸 Instagram: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.instagram.com\u002Fsuperdevacademy\u002F\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(เกร็ดความรู้สั้นๆ และเบื้องหลังการทำงาน)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 TikTok: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(Tips &amp; Tricks ฉบับย่อยง่าย)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🌐 Website: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"http:\u002F\u002Fsuperdevacademy.com\">\u003Cstrong>superdevacademy.com\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(คลังบทความและคอร์สเรียนฉบับเต็ม)\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003C\u002Fp>","41igysv7qh8n_2ke79qhemk.png","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002Ftuumsgk0t62dz95\u002F41igysv7qh8n_2ke79qhemk.png","2026-07-13 14:49:41.511Z","76qprkevbgfdps8",{"keywords":15,"locale":54,"school_blog":64},[16,23,28,32,36,41,46,50],{"collectionId":17,"collectionName":18,"created":19,"created_by":13,"id":20,"name":21,"updated":22,"updated_by":13},"sclkey987654321","school_keywords","2026-03-04 08:20:14.253Z","ah6lvy4x8qe08l5","Golang","2026-06-07 06:45:08.193Z",{"collectionId":17,"collectionName":18,"created":24,"created_by":13,"id":25,"name":26,"updated":27,"updated_by":13},"2026-03-04 08:20:11.547Z","ey3puyme01a9bsw","Go","2026-06-07 06:45:07.798Z",{"collectionId":17,"collectionName":18,"created":29,"created_by":13,"id":30,"name":31,"updated":29,"updated_by":13},"2026-07-13 14:48:08.602Z","ssabke831bu5mq9","Worker Pools",{"collectionId":17,"collectionName":18,"created":33,"created_by":13,"id":34,"name":35,"updated":33,"updated_by":13},"2026-07-13 14:48:12.384Z","ehqmnfqtgta8ctq","Async Tasks",{"collectionId":17,"collectionName":18,"created":37,"created_by":13,"id":38,"name":39,"updated":40,"updated_by":13},"2026-05-19 09:09:15.823Z","fbj34lco59k2lc0","Go Channels","2026-06-07 06:49:16.397Z",{"collectionId":17,"collectionName":18,"created":42,"created_by":13,"id":43,"name":44,"updated":45,"updated_by":13},"2026-03-04 08:33:58.044Z","nb6p1r8sfqlsxf8","Goroutines","2026-06-07 06:45:54.913Z",{"collectionId":17,"collectionName":18,"created":47,"created_by":13,"id":48,"name":49,"updated":47,"updated_by":13},"2026-07-13 14:48:39.846Z","dbdjz8l7hwdmsvv","AI Queue",{"collectionId":17,"collectionName":18,"created":51,"created_by":13,"id":52,"name":53,"updated":51,"updated_by":13},"2026-07-13 14:48:44.742Z","4wyo6nmcvy370oc","Backend Optimization",{"code":55,"collectionId":56,"collectionName":57,"created":58,"flag":59,"id":60,"is_default":61,"label":62,"updated":63},"th","pbc_1989393366","locales","2026-01-22 10:59:55.832Z","twemoji:flag-thailand","s8wri3bt4vgg2ji",true,"Thai","2026-04-10 15:42:46.614Z",{"category":65,"collectionId":66,"collectionName":67,"created":68,"expand":69,"id":84,"slug":85,"updated":86,"views":87},"wqxt7ag2gn7xcmk","pbc_2105096300","school_blogs","2026-07-13 14:49:39.194Z",{"category":70},{"blogIds":71,"collectionId":72,"collectionName":73,"created":74,"created_by":13,"id":65,"image":75,"image_alt":76,"image_path":77,"label":78,"name":79,"priority":80,"publish_at":81,"scheduled_at":76,"status":82,"updated":83,"updated_by":13},[],"sclcatblg987654321","school_category_blogs","2026-03-04 08:33:53.210Z","59ty92ns80w_15oc1implw.png","","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Fwqxt7ag2gn7xcmk\u002F59ty92ns80w_15oc1implw.png",{"en":79,"th":79},"Golang The Series",1,"2026-03-16 04:39:38.440Z","published","2026-06-07 06:45:03.856Z","9ml9zd4i9t06yo5","golang-the-series-ep161-async-ai-tasks-worker-pools","2026-07-21 07:34:40.367Z",130,"tuumsgk0t62dz95",[20,25,30,34,38,43,48,52],"2026-07-20 02:31:35.969Z","เรียนรู้วิธีสร้าง Worker Pools ใน Go เพื่อจัดการคิวงาน AI และ Async Tasks เบื้องหลัง ช่วยป้องกันปัญหาคอขวดบนเซิร์ฟเวอร์ ควบคุมการใช้ Memory (OOM) และทำ Rate Limiting ในตัว","Golang The Series EP.161: Async AI Tasks การใช้ Worker Pools จัดการคิวงาน AI","2026-07-20 02:31:35.971Z",{"th":85,"en":85}]