空結構體
空結構體的寬度是0,佔用了0字節的內存空間。
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0
由於空結構體佔用0字節,那麼空結構體也不需要填充字節。所以空結構體組成的組合數據類型也不會佔用內存空間。
type S struct {
A struct{}
B struct{}
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 0
0人點贊
Go
chan struct{}
通過消息來共享數據是golang的一種設計哲學,channel則是這種哲理的體現。
golang中的空結構體 channel := make(chan struct{})
特點
- 省內存,尤其在事件通信的時候。
- struct零值就是本身,讀取close的channel返回零值
常用用法
通常struct{}類型channel的用法是使用同步,一般不需要往channel裏面寫數據,只有讀等待,而讀等待會在channel被關閉的時候返回。
type Miner struct {
api api.FullNode
epp gen.WinningPoStProver
lk sync.Mutex
address address.Address
stop chan struct{}
stopping chan struct{}
waitFunc waitFunc
lastWork *MiningBase
minedBlockHeights *lru.ARCCache
}
stop 它是一個管道chan,內部的數據類型是struct{}。
單獨拿struct{}來說,我們熟悉type Name struct{a int, b bool}這樣去定義一個結構體的類型,其實struct{…}就是定義結構體,和map[string]int這種定義是一樣的,type只是給它取了一個別名。 總結: 實際上struct{}就是一種普通數據類型,只是沒有具體的值而已。
注意,channel對象一定要make出來才能使用。, 如下,make後賦值給m
func (m *Miner) Start(ctx context.Context) error {
m.lk.Lock()
defer m.lk.Unlock()
if m.stop != nil {
return fmt.Errorf("miner already started")
}
m.stop = make(chan struct{})
go m.mine(context.TODO())
return nil
}
func (m *Miner) Stop(ctx context.Context) error {
m.lk.Lock()
defer m.lk.Unlock()
m.stopping = make(chan struct{})
stopping := m.stopping
close(m.stop)
select {
case <-stopping:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
使用場景
首先事件通知,可以通過寫入 通知其他協程,但是隻能通知一個。
channel := make(chan struct{})
go func() {
// ... do something
channel <- struct{}{}
}()
fmt.Println(<-channel)
和close進行配合,通知所有相關協程。
在讀入被close的channel返回零值,正常的協程是讀取不到這個close的。
close之後,所有協程都可以讀到。
比較經典的例子就是用於stopChan作爲停止channel通知所有協程。
在下面的例子中 我們可以通過s.Stop()通知所有的serverHandler協程停止工作,並且等待他們正常退出。
type Server struct {
serverStopChan chan struct{}
stopWg sync.WaitGroup
}
func (s *Server) Stop() {
if s.serverStopChan == nil {
panic("gorpc.Server: server must be started before stopping it")
}
close(s.serverStopChan)
s.stopWg.Wait()
s.serverStopChan = nil
}
func serverHandler(s *Server){
for {
select {
case <-s.serverStopChan:
return
default:
// .. do something
}
}
}
帶緩衝的chan struct{}數據讀寫
另外也可以定義帶緩衝的channel
package main
import (
"time"
"log"
)
var ch chan struct{} = make(chan struct{}, 2)
func foo() {
ch <- struct{}{}
log.Println("foo() 000");
ch <- struct{}{}
log.Println("foo() 111");
time.Sleep(5 * time.Second)
log.Println("foo() 222");
close(ch)
log.Println("foo() 333");
}
func main() {
var b struct{}
log.Println("main() 111");
go foo()
log.Println("main() 222");
a := <-ch
log.Println("main() 333", a);
b = <-ch
log.Println("main() 444", b);
c := <-ch
log.Println("main() 555", c);
}
<-ch用來從channel ch中接收數據,這個表達式會一直被block,直到有數據可以接收。
從一個nil channel中接收數據會一直被block。(往nil channel中發送數據會一致被阻塞着。)
從一個被close的channel中接收數據不會被阻塞,而是立即返回,接收完已發送的數據後會返回元素類型的零值(zero value)。
如前所述,你可以使用一個額外的返回參數來檢查channel是否關閉。
x, ok := <-ch
x, ok = <-ch
var x, ok = <-ch
如果OK 是false,表明接收的x是產生的零值,這個channel被關閉了或者爲空。