2012-07-05 107 views
13

我知道我可以在Go中使用select {}語法在多個通道上等待,並使用syscall.Select()或類似函數等待多個文件描述符。但是可以一次等待兩個頻道嗎?在Go中可以同時等待兩個通道和文件描述符嗎?

對於背景,我想有一個接受用於在信道消息和通過套接字連接(由gozmq提供)轉發他們的goroutine,同時等待套接字連接上回復。

由於底層庫的線程安全性要求,套接字一次只能在一個線程中訪問,這就是爲什麼我想知道是否有辦法從一個goroutine處理這個問題。

+0

只是爲了好奇,這是你可以在其他項目中做的事情,比如https://github.com/duckie/boson – 2017-06-19 10:02:26

回答

10

選擇通道和文件描述符是不可能的,因爲抽象是處於不同的級別。通道由操作系統的運行時間和文件描述符處理。你需要的是在它們之間架起一座橋樑,這可以通過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將在線程中安全運行。

+0

聽起來好像它會工作:在另一個線程中執行'select'或'poll'聽起來不會對zmq的線程安全要求造成嚴重破壞,因此在goroutine中執行該操作並通過通道返回結果聽起來像是好的解決方案謝謝! – 2012-07-06 00:34:41

1

Go中的所有net.Conn都可以同時訪問。如果你的圖書館是基於此的,那就不應該有任何問題。

否則,在Go中每個連接產生一個goroutine是很常見的。這個goroutine通常只負責從套接字讀取數據(可能會阻塞),並使用通道將所有數據轉發到另一個協調器goroutine。然後該協調器goroutine可以使用select語句與所有這些套接字連接(包裝在通道中)以及其他事件進行交互。另請注意,您可以輕鬆地將緩衝區添加到這些通道,以便慢速客戶端無法阻止您的協調器參數。

+0

這個庫不是基於net.Conn的。它特別指出,套接字不能同時用於多個線程:http://api.zeromq.org/2-2:zmq-socket。我無法阻止嘗試讀取套接字,因爲我可能需要在收到任何數據之前先寫入套接字。 – 2012-07-05 13:25:38

2

菌種爲要等待每個fd一個新的goroutine,讓他們送fd一個通道時,他們讀到的東西,選擇的渠道。

+0

你如何告訴這些goroutine停止?關閉fd不是可靠的方法。 – chmike 2016-09-29 08:51:05

+0

@chmike如果關閉文件描述符不會導致您的線程被通知 - 向您的操作系統團隊提交錯誤。 IOW,關閉一個FD是一個記錄,可靠的方式來通知該過程。 – 2016-09-29 19:32:17

+0

如果在讀取中被阻塞,則會通知線程/進程。否則它不是。如果fd關閉,內核可以自由重用。如果發生這種情況,goroutine將不知道關閉,並可能開始從重新使用的fd標識的另一個源讀取數據。爲此,唯一可靠的解決方案是在fd上使用select和用於發送通知字節的管道。 – chmike 2016-09-30 11:46:32