【Golang】关于Go中的struct{}

一、关于struct {}

struct是Go中的关键字,用于定义结构类型

1
2
3
4
type User struct {
    Name string
    Age int
}

struct {} :表示struct类型

struct {} 是一种普通数据类型,一个无元素的结构体类型,通常在没有信息存储时使用。

优点是大小为0,不需要内存来存储struct {}类型的值。

struct {} {}:表示struct类型的值,该值也是空。
struct {} {}是一个复合字面量,它构造了一个struct {}类型的值,该值也是空。

1
2
3
4
5
6
7
8
9
10
11
12
var set map[string]struct{}
set = make(map[string]struct{})
 
// Add some values to the set:
set["red"] = struct{}{}
set["blue"] = struct{}{}
 
// Check if a value is in the map:
_, ok := set["red"]
fmt.Println("Is red in the map?", ok)
_, ok = set["green"]
fmt.Println("Is green in the map?", ok)

输出内容

1
2
Is red in the map? true
Is green in the map? false

空结构体的宽度是0,占用了0字节的内存空间

1
2
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0

由于空结构体占用0字节,那么空结构体也不需要填充字节。所以空结构体组成的组合数据类型也不会占用内存空间。

1
2
3
4
5
6
7
type S struct {
    A struct{}
    B struct{}
}
 
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 0

二、chan struct{}

通过消息来共享数据是golang的一种设计哲学,channel则是这种哲理的体现。

golang中的空结构体 channel := make(chan struct{})

  • 省内存,尤其在事件通信的时候。

  • struct零值就是本身,读取close的channel返回零值

1、通常struct{}类型channel的用法是使用同步

注意,channel对象一定要make出来才能使用

一般不需要往channel里面写数据,只有读等待,而读等待会在channel被关闭的时候返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main
 
import (
    "time"
    "log"
)
 
var ch chan struct{} = make(chan struct{})
 
func foo() {
    log.Println("foo start");
    time.Sleep(3 * time.Second)
    log.Println("foo close chan");
    close(ch)
    log.Println("foo end");
}
 
func main() {
    log.Println("main start");
    go foo()
    log.Println("main wait chan");
    <-ch
    log.Println("main end");
}

运行结果

1
2
3
4
5
6
2022/05/28 19:34:44 main start
2022/05/28 19:34:44 main wait chan
2022/05/28 19:34:44 foo start
2022/05/28 19:34:47 foo close chan
2022/05/28 19:34:47 foo end
2022/05/28 19:34:47 main end

如果一开始就往 元素类型为 struct{} 的 chan 中写元素,则 main 中会立马收到信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main
 
import (
    "time"
    "log"
)
 
var ch chan struct{} = make(chan struct{})
 
func foo() {
    log.Println("foo start");
    ch <- struct{}{}
    time.Sleep(3 * time.Second)
    log.Println("foo close chan");
    close(ch)
    log.Println("foo end");
}
 
func main() {
    log.Println("main start");
    go foo()
    log.Println("main wait chan");
    ret, ok := <-ch
    if ok {
        log.Println("main recv info : ", ret);
    } else {
        log.Println("main chan has closed");
    }
 
    log.Println("main end");
} 

运行结果

1
2
3
4
5
2022/05/28 19:35:41 main start
2022/05/28 19:35:41 main wait chan
2022/05/28 19:35:41 foo start
2022/05/28 19:35:41 main recv info :  {}
2022/05/28 19:35:41 main end

2、比较经典的例子就是用于stopChan作为停止channel通知所有协程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
        }
    }
}

3、带缓冲的chan struct{}数据读写

另外也可以定义带缓冲的channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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是否关闭。

1
2
3
x, ok := <-ch
x, ok = <-ch
var x, ok = <-ch

如果OK 是false,表明接收的x是产生的零值,这个channel被关闭了或者为空。

 

posted @   踏雪无痕SS  阅读(862)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 字符编码:从基础到乱码解决
历史上的今天:
2019-05-28 【Java】3到5年开发常见的Java面试题
2019-05-28 【Linux】小米路由开启SSH访问权限
2015-05-28 PHP实现4种排序算法
点击右上角即可分享
微信分享提示