• 【Golang】关于Go中的struct{}


    一、关于struct {}

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

    type User struct {
        Name string
        Age int
    }

    struct {} :表示struct类型

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

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

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

    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)

    输出内容

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

    空结构体的宽度是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

    二、chan struct{}

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

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

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

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

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

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

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

    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");
    }

    运行结果

    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 中会立马收到信息

    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");
    } 

    运行结果

    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通知所有协程

    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

    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被关闭了或者为空。

  • 相关阅读:
    由u盘安装Ubuntu引出的事件
    初试Ubuntu
    从error 中学习
    快手一面:牛客:字符串左移
    快手一面:Leetcode:最小栈
    十三、线程池
    十二、windows临界区、其他各种mutex互斥量
    十一、std::async深入
    LeetCode(703):找出数据流中的第K大元素
    LeetCode(1003):检查替换后的字符串
  • 原文地址:https://www.cnblogs.com/chenpingzhao/p/16321720.html
Copyright © 2020-2023  润新知