我知道我可以在Go中使用select {}
語法在多個通道上等待,並使用syscall.Select()
或類似函數等待多個文件描述符。但是可以一次等待兩個頻道嗎?在Go中可以同時等待兩個通道和文件描述符嗎?
對於背景,我想有一個接受用於在信道消息和通過套接字連接(由gozmq提供)轉發他們的goroutine,同時等待套接字連接上回復。
由於底層庫的線程安全性要求,套接字一次只能在一個線程中訪問,這就是爲什麼我想知道是否有辦法從一個goroutine處理這個問題。
我知道我可以在Go中使用select {}
語法在多個通道上等待,並使用syscall.Select()
或類似函數等待多個文件描述符。但是可以一次等待兩個頻道嗎?在Go中可以同時等待兩個通道和文件描述符嗎?
對於背景,我想有一個接受用於在信道消息和通過套接字連接(由gozmq提供)轉發他們的goroutine,同時等待套接字連接上回復。
由於底層庫的線程安全性要求,套接字一次只能在一個線程中訪問,這就是爲什麼我想知道是否有辦法從一個goroutine處理這個問題。
選擇通道和文件描述符是不可能的,因爲抽象是處於不同的級別。通道由操作系統的運行時間和文件描述符處理。你需要的是在它們之間架起一座橋樑,這可以通過net.Pipe()
完成。
差不多,你需要做的是奉獻一個夠程到epoll()
/select()
看你ZMQ的套接字和一個「喚醒」 net.Pipe()
。這是您的投票服務器。另一個goroutine會監聽您的讀寫通道。當有人在讀或寫通道上發送數據時,第二個goroutine將通過管道發送喚醒poll服務器。
這就是標準庫中的淨包的工作原理。我強烈建議閱讀它以獲取靈感(或竊取...... BSD許可證非常寬鬆)。
這是從net本身對pollServer的描述。您可能需要閱讀代碼才能理解這是什麼意思,但fd.go
中的這一部分應該是一個開始尋找的好地方。
// A pollServer helps FDs determine when to retry a non-blocking
// read or write after they get EAGAIN. When an FD needs to wait,
// send the fd on s.cr (for a read) or s.cw (for a write) to pass the
// request to the poll server. Then receive on fd.cr/fd.cw.
// When the pollServer finds that i/o on FD should be possible
// again, it will send fd on fd.cr/fd.cw to wake any waiting processes.
// This protocol is implemented as s.WaitRead() and s.WaitWrite().
//
// There is one subtlety: when sending on s.cr/s.cw, the
// poll server is probably in a system call, waiting for an fd
// to become ready. It's not looking at the request channels.
// To resolve this, the poll server waits not just on the FDs it has
// been given but also its own pipe. After sending on the
// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a
// byte to the pipe, causing the pollServer's poll system call to
// return. In response to the pipe being readable, the pollServer
// re-polls its request channels.
//
// Note that the ordering is "send request" and then "wake up server".
// If the operations were reversed, there would be a race: the poll
// server might wake up and look at the request channel, see that it
// was empty, and go back to sleep, all before the requester managed
// to send the request. Because the send must complete before the wakeup,
// the request channel must be buffered. A buffer of size 1 is sufficient
// for any request load. If many processes are trying to submit requests,
// one will succeed, the pollServer will read the request, and then the
// channel will be empty for the next process's request. A larger buffer
// might help batch requests.
//
// To avoid races in closing, all fd operations are locked and
// refcounted. when netFD.Close() is called, it calls syscall.Shutdown
// and sets a closing flag. Only when the last reference is removed
// will the fd be closed.
祝你好運重新執行網絡。所有這一切結束時的好消息,您的zmq-socket將在線程中安全運行。
聽起來好像它會工作:在另一個線程中執行'select'或'poll'聽起來不會對zmq的線程安全要求造成嚴重破壞,因此在goroutine中執行該操作並通過通道返回結果聽起來像是好的解決方案謝謝! – 2012-07-06 00:34:41
Go中的所有net.Conn都可以同時訪問。如果你的圖書館是基於此的,那就不應該有任何問題。
否則,在Go中每個連接產生一個goroutine是很常見的。這個goroutine通常只負責從套接字讀取數據(可能會阻塞),並使用通道將所有數據轉發到另一個協調器goroutine。然後該協調器goroutine可以使用select語句與所有這些套接字連接(包裝在通道中)以及其他事件進行交互。另請注意,您可以輕鬆地將緩衝區添加到這些通道,以便慢速客戶端無法阻止您的協調器參數。
這個庫不是基於net.Conn的。它特別指出,套接字不能同時用於多個線程:http://api.zeromq.org/2-2:zmq-socket。我無法阻止嘗試讀取套接字,因爲我可能需要在收到任何數據之前先寫入套接字。 – 2012-07-05 13:25:38
菌種爲要等待每個fd
一個新的goroutine,讓他們送fd
一個通道時,他們讀到的東西,選擇的渠道。
你如何告訴這些goroutine停止?關閉fd不是可靠的方法。 – chmike 2016-09-29 08:51:05
@chmike如果關閉文件描述符不會導致您的線程被通知 - 向您的操作系統團隊提交錯誤。 IOW,關閉一個FD是一個記錄,可靠的方式來通知該過程。 – 2016-09-29 19:32:17
如果在讀取中被阻塞,則會通知線程/進程。否則它不是。如果fd關閉,內核可以自由重用。如果發生這種情況,goroutine將不知道關閉,並可能開始從重新使用的fd標識的另一個源讀取數據。爲此,唯一可靠的解決方案是在fd上使用select和用於發送通知字節的管道。 – chmike 2016-09-30 11:46:32
只是爲了好奇,這是你可以在其他項目中做的事情,比如https://github.com/duckie/boson – 2017-06-19 10:02:26