การใช้ Channel

Go Channel คืออะไร ?


Channel มันคือ ท่อการสื่อสารระหว่าง Go Routines กับ ตัวโปรแกรมหลัก หรือ Go Routines ตัวอื่นๆก็ได้ หรือพูดง่ายๆ ว่า เวลาเราสั่งงาน Go Routines ให้ทำอะไร มันจะไปทำงานเบื้องหลัง คือแบ่งไปให้ CPU อีก Core ช่วยทำงาน

ส่วนตัวโปรแกรมหลักก็ทำงานไป และ เมื่อตัว Go Routines ทำงานเสร็จ แล้วต้องการส่งค่าอะไรบางอย่าง มาที่ตัวโปรแกรมหลัก Channel จะเป็นท่อน้ำ ที่ไหลค่าเหล่านั้นจาก Go Routines มาตัวโปรแกรมหลัก และเอาค่าไปใช้งานต่อ

ในบทความที่แล้ว การเข้ารหัส / ถอดรหัส JSON เราได้เรียนรู้เกี่ยวกับ Go-Betweens และวิธีที่ช่วยให้เราเรียกใช้งานต่างๆ แยกกันได้ พร้อมกัน แต่เมื่อคุณเริ่มใช้กิจวัตร go คุณมีข้อกังวลเพิ่มเติมบางอย่าง เช่น การสื่อสารระหว่างกัน เราจำเป็นต้องหลีกเลี่ยงสถานการณ์ที่กิจวัตรประจำวันสองรายการขึ้นไปแก้ไขข้อมูลที่ใช้ร่วมกันเดียวกันในเวลาเดียวกัน

ดังนั้นในกรณีของเราในบทความที่แล้ว เรากำลังเข้าถึงการเชื่อมต่อซ็อกเก็ตเว็บเป็นรูทีน go ซึ่งอาจนำไปสู่สภาพการแข่งขัน มีคำกล่าวที่ว่าอย่าสื่อสารด้วยการแบ่งปันความทรงจำ แทนที่จะแบ่งปันความทรงจำด้วยการสื่อสาร ดังนั้นสิ่งนี้หมายความว่าอย่างไร


โดยการประกาศตัวแปร Channel เขียนดังนี้

msgChan := make(chan string)


การใส่ค่าเข้าตัวแปร msgChan หรือ เข้าท่อ ของ Channel 

msgChan <- "Hello"


การรับค่าตัวแปรจาก Channel

msg := <-msgChan



โค้ดด้านล่าง เรากำลังสร้างท่อ Channel ท่อนึง ที่ชื่อ msgChan ซึ่งท่อตัวนี้ จะมีแค่ข้อมูล ประเภท String ที่สามารถไหลเข้าออกจากท่อได้เท่านั้น ถ้าเราต้องการเปลี่ยนเป็น ประเภทอื่น  ต้องเปลี่ยนประเภท หลัง chan


สร้างไฟล์ client.go และ package main กันก่อน แล้วมาสร้างฟังก์ชั่น main ในไฟล์ไคลเอนต์กัน

package main

import (
	"fmt"
)

func main() {
	msgChan := make(chan string)
	go func() {
		msgChan <- "Hello"
	}()
	msg := <-msgChan
	fmt.Println(msg)
}

และ ที่ฟังก์ชั่น main จะกำหนดตัวแปร message ใหม่ ชื่อ chan ให้กับ channel ใหม่ที่ส่งสตริงซึ่ง ทำได้โดย make และ imprint ของ channel ตามด้วยประเภทข้อมูล channel ผ่านได้และกรณีง่ายๆ นี้จะตั้งค่าเป็นสตริง

ต่อไป ฉันจะสร้างฟังก์ชันที่ไม่มีชื่อหรือไม่ระบุชื่อที่เรียกใช้ทันที แต่จะใส่ไว้ใน go Routine ของตัวเองโดยใช้คีย์เวิร์ด go ภายในฟังก์ชัน ฉันจะส่งสตริงใหม่ลงในช่องหรือไปป์ทำให้ข้อความชื่อตัวแปรช่อง Chan บน ด้านซ้ายจากนั้นฉันจะใช้ตัวดำเนินการลูกศรซ้ายตามด้วยสตริงเพื่อส่ง Hello

ตอนนี้หลังจากฟังก์ชันที่ไม่ระบุตัวตนนี้ ฉันจะดึงสตริงออกจากช่องหรือไพพ์โดยการตั้งค่าใหม่ โคลอนตัวแปรเท่ากับลูกศรซ้ายตามด้วยตัวแปรแชนเนลข้อความตัวแปร จากนั้นฉันจะพิมพ์ข้อความ

ตอนนี้ให้เรียกใช้ไฟล์ go run client.go แล้วเราจะเห็น Hello ถูกพิมพ์ออกมา

ตกลงโดยสรุปแล้วสตริง Hello ถูกสร้างขึ้นใน Doherty’s แยกต่างหากกว่าที่ถูกส่งไปยังต้นฉบับ go Routine ที่เรียกใช้ฟังก์ชัน main ผ่าน channel แล้วพิมพ์ ฉันคิดว่าคำอุปมาอุปไมยเป็นสิ่งที่ดี คุณใส่ค่าที่ปลายด้านหนึ่งและออกมาอีกด้านหนึ่งและไม่มีความเสี่ยงของกิจวัตรประจำวันที่จะไป เข้าถึงค่าใด ๆ ที่ส่งผ่านในเวลาเดียวกัน

กล่าวอีกนัยหนึ่ง Channel เป็นวิธีที่ปลอดภัยในการส่งผ่านค่าระหว่าง Go-Betweens

มาสร้างตัวอย่างที่สมจริงมากขึ้นกันเถอะ สิ่งที่เราพยายามจะทำ ไปข้างหน้าและสร้าง client struck ในโครงสร้างนี้สำหรับตอนนี้คือ sin which ซึ่งเป็น channel ที่เราจำเป็นต้องกำหนดประเภทที่ channel สามารถผ่านได้ ดังนั้นคำถามคือสิ่งที่เราต้องการจะผ่านพ้นไป

ฉันต้องการส่งข้อความขาออกผ่านช่อง แล้วเราจะมีรูทีนครั้งเดียวที่อ่านอะไรก็ตามที่ผ่านช่องทางการส่งและส่งต่อข้อความนั้นไปยังลูกค้าผ่านทางเว็บ


คุณแทบจะนึกถึงช่องทางการส่งนี้แทนวิธีการส่งที่ถูกต้องแทนการโทร ส่งและส่งข้อความตามคุณกำลังส่งข้อความผ่านช่องโดยใช้ลูกศรซ้ายโอเปอเรเตอร์ กลับไปที่คำถามในมือ เราต้องการส่งข้อความประเภทใดผ่านช่องข้อความ

ดูเหมือนว่าเป็นทางเลือกที่ดีสำหรับฉัน เราได้สร้างโครงสร้างข้อความในไฟล์ go หลักแล้ว มีการใช้โครงสร้างนั้นในไคลเอนต์นี้ชั่วคราว go file สร้างสามวิธีสำหรับโครงสร้างคลาสโดยมีจุดประสงค์ในการรันแต่ละวิธีใน goroutine แยกต่างหาก วิธีแรกถูกต้อง นี่คือวิธีการที่จะใช้ในการส่งข้อความไปยังไคลเอนต์ผ่านเว็บซ็อกเก็ตในที่สุด

ใช่เป็นเพียงฟังก์ชันเท่านั้น ยกเว้นว่าเรากำลังใช้ตัวรับในวงเล็บซึ่งทำให้เป็น วิธีการของ client Monir แสดงให้คุณเห็นว่าคุณสามารถดึงค่าเดียวออกจากช่องโดยใช้ลูกศรซ้าย คุณยังสามารถใช้คีย์เวิร์ด range เพื่อวนซ้ำค่าที่ส่งผ่านแชนเนล go และเมื่อมี ไม่มีช่องที่ถูกส่งไป บล็อกประจำ go ที่นี่รอการส่งค่าใหม่

นี่คือวิธีที่เราจะใช้มันเราจะสร้าง for loop โดยไม่กำหนดข้อความตัวแปรโดยใช้ call in เท่ากับ ตามด้วย range คีย์เวิร์ด และ channel ไม่สามารถส่งแบบวนซ้ำได้



ในที่สุดเราจะเรียกซ็อกเก็ตส่งเจสัน แต่ตอนนี้ขอเพียงพิมพ์ข้อความไปยังวิธีการของเรา เขียน เราจะเขียนช่องสมัคร วิธีที่คุณสามารถคิดว่าวิธีนี้เป็นสถานที่หรือเราจะเริ่มต้นการสืบค้นฐานข้อมูลที่จะสตรีมช่อง การเปลี่ยนแปลง เช่น การแก้ไขโฆษณาและการลบ

อย่างที่ฉันพูดไปก่อนหน้านี้ ฉันยังไม่อยากพูดถึงเรื่อง D-B ล่าสุดเลย ลองจำลองสิ่งนี้โดยการเขียน for loop ที่สุ่มเวลาบางส่วน จากนั้นมันจะ ส่งข้อความผ่านช่องส่งถูกใส่ในแพ็คเกจเวลาและจะเรียกการนอนเวลาซึ่งจะ หยุดกิจวัตรการเดินทางชั่วคราวเมื่อจำเป็นต้องผ่านและช่วงเวลาเพื่อเข้าสู่โหมดสลีปของฟังก์ชัน

ลองเขียนฟังก์ชัน throwaway ชื่อ R ซึ่งจะคืนค่าการวนซ้ำระหว่าง 0 ถึง 1 วินาที และในฟังก์ชันระยะเวลา ฉันจะเรียกแรนดอล์ และในการส่งผ่านค่าสูงสุด 1,000 สิ่งนี้ควรสร้างตัวเลขสุ่มระหว่าง 0 ถึง 1,000

กลับไปที่เวลาของเราเกี่ยวกับการเรียกใช้ฟังก์ชัน sleep เราเรียกฟังก์ชันของเราซึ่งโดยทั่วไปจะให้การสุ่ม ระยะเวลานานถึงหนึ่งวินาทีหลังจากระยะเวลาการนอนหลับ เราจะเขียนข้อความเพื่อจำลอง กำลังเพิ่มช่องใหม่และสตรีมกลับไปยัง client การทำเช่นนี้จะทำให้ go channel client dots ของเรา และในกรณีนี้ เราจะใช้ลูกศรซ้ายและสร้างวัตถุข้อความใหม่ที่มีชื่อ channel

เพิ่ม มาคัดลอกฟังก์ชันทั้งหมดนี้ จากนั้นเราจะเปลี่ยนชื่อฟังก์ชันที่คัดลอกเพื่อสมัครรับข้อความฟังก์ชัน ที่จะจำลองเหตุการณ์ข้อความแอพใหม่และขอเปลี่ยนชื่อเป็นข้อความ และตอนนี้ เรามีสามวิธีที่สามารถทำได้และควรรันในรูทีน go แยกกัน แต่วิธีการสมัครแต่ละวิธีสามารถส่งข้อความจากรูทีนไปยังรูทีน go ได้อย่างปลอดภัย จะใช้วิธีการที่ถูกต้อง


กลับมาที่หน้าที่หลักกันอีกครั้ง เราจำเป็นต้องสร้างลูกค้าใหม่ เราสามารถทำได้ด้วยตนเอง แต่มันค่อนข้างละเอียดเมื่อพิจารณาว่าเราจะต้องสร้าง go challe และในที่สุด มากขึ้น แต่มีแบบแผนทั่วไปและไปเราสามารถปฏิบัติตามเพื่อปรับปรุงกระบวนการสร้างที่ซับซ้อนมากขึ้น

Go ไม่มีคอนสตรัคเตอร์ซึ่งสะดวกสำหรับการเริ่มต้นวัตถุที่ซับซ้อนแทนคุณ สร้างฟังก์ชันที่จะสร้างวัตถุใหม่และส่งคืน

ดังนั้นฉันจะสร้างฟังก์ชันใหม่ที่ชื่อว่าไคลเอนต์ใหม่ที่ส่งคืนตัวชี้ไคลเอนต์ จากนั้นในฟังก์ชัน ฉันจะส่งคืนตัวชี้สำหรับลูกค้าที่สร้างอินสแตนซ์ใหม่

ฉันสร้างช่องส่งใหม่คล้ายกับที่เราเคยทำก่อนหน้านี้สร้างสำนักพิมพ์ CS Chan และข้อความ ไวยากรณ์นี้แตกต่างไปจากที่เราเคยใช้มาเล็กน้อย ตัวเลือกแบบละเอียดนี้อ้างอิงอย่างชัดเจนว่าฟิลด์ส่งและกำหนดค่าหลังโคลอน

อีกหนึ่ง gotchas เล็กน้อยที่คุณต้องมีเครื่องหมายจุลภาคลงท้าย แม้ว่าจะเป็นช่องสุดท้ายในรายการช่องก็ตาม

ตอนนี้เพื่อสร้างตัวอย่างไคลเอนต์ใหม่ ฉันสามารถตั้งค่าโคลอนตัวแปรไคลเอนต์เท่ากับสิ่งที่ส่งคืนโดยการเรียก

new client.


package main

import (
	"fmt"
	"math/rand"
	"time"
)

type Message struct {
	Name string      `json:"name"`
	Data interface{} `json:"data"`
}

type Client struct {
	send chan Message
}

func (client *Client) write() {
	for msg := range client.send {
		// TODO: socket.sendJSON(msg)
		fmt.Printf("%#v\n", msg)
	}
}

func (client *Client) subscribeChannels() {
	// TODO: changefeed Query Query RethinkDB
	for {
		time.Sleep(r())
		client.send <- Message{"channel add", ""}
	}
}

func (client *Client) subscribeMessages() {
	// TODO: changefeed Query Query RethinkDB
	for {
		time.Sleep(r())
		client.send <- Message{"message add", ""}
	}
}

func r() time.Duration {
	return time.Millisecond * time.Duration(rand.Intn(1000))
}

func NewClient() *Client {
	return &Client{
		send: make(chan Message),
	}
}

func main() {
	client := NewClient()
	go client.subscribeChannels()
	go client.subscribeMessages()
	client.write()
}


ต่อไป มาเริ่มวิธีการช่องที่สมัครรับข้อมูลในรูทีน go ของตัวเอง ตอนนี้เริ่มวิธีการสมัครข้อความในผู้ว่าการของตัวเอง

และคุณจะเห็นข้อความที่สร้างขึ้นในแต่ละฟังก์ชั่นการสมัครรับข้อมูลถูกพิมพ์เป็นประจำทางขวา

Leave a Reply

Your email address will not be published. Required fields are marked *