今天呆在出租屋,在吃饭。
看到了xx留在这里放东西的一个袋子,是甜啦啦的袋子。
学校好像也有一家。
记得之前晚上一直到6点半,大家一块去吃饭,在路上,h师傅就问说吃什么,zz说我们去吃甜啦啦。
h师傅说,神经。
晚上因为是冬天,天已经完全黑了,还是会有点小冷。我看着软件园食堂五颜六色的招牌,觉得回不去了。

最近一个人呆在出租屋里面的时间比较长。大部分时间也都是花在做项目上,整个人也都挺焦虑的。
很久没有写博客了。我高中的时候一到焦虑就会写一点东西,花上20到30分钟。
现在很多时候都是发呆的时间多点,也没有想什么事情。
可能也是因为我睡眠不是很足吧。
项目的前端部分到底还是外包给了一个认识的人,他刚好会一点unity和c#。
我还是挺幸运的,看到他朋友圈里有发vs的编辑器,猜到是写的c#,就随口问了一下,没有想到真的会。
而且需求说完之后,也算是想接。他是一个很认真的人,今天集训的时候,我和他是一个队伍里的,打比赛。
看到他百般尝试的样子,我有一种说不上来的羡慕。
我好像已经失去了这种耐心。大脑不知道在什么时候开始已经变得很僵硬了,转不动。只会守着落伍的思想预设来思考。我注定是不会有什么成就的。
大一也稀里糊涂的过去了,马上大二了。
大二可能也是这样吧。
思念没有涌上心头,我变得越发不会感慨。
只是保留了哭的能力,有的时候也不知道是因为什么哭。
我越是长大,越是会想对父母好点。一些刻板映像,像是报喜不报忧,像是按照他们想要的想法去说,总的来说就是不让他们担心,并且尽可能的让他们觉得我在外面过的很好,发展的也很好,做出了什么成就,拿了什么奖,有还算是不错的交际能力,和自理能力。
小时候的记忆慢慢忘却了,长大了只记得父母的好了,想到最后我能依赖的也只有父母,就觉得对他们再怎么好也不为过。
时间在感知上确实是越来越快了。
有的时候好像没做什么,就是这边拉个会,那边回个消息,再记点东西,时间就过去了。
我到现在也还是没能学会画画,学会唱歌,学会至少一种乐器。
生活也渐渐变得随意,比较关心的其实还是吃什么,然后就是晚上吃什么。
有的时候也会刷刷淘宝,看到一个很想要的东西,衣服什么的,钱是够的,因为有补贴,做项目,但是还是会觉得下次吧,下次再买。
感觉也不会怎么做梦了,晚上也睡不着,经常会到2点多才能睡着。我觉得我现在脑子坏掉和这个肯定是脱不开干系的。
有的时候其实还是会想跳,但是还是觉得自己身上还挂着好多东西,至少还得对别人多负点责,不要那么想不开。然后就会偶尔点个外卖,奢侈一下。
也不知道以后会怎么样,可能都没有以后了。
我总觉得我还差了一点什么,一个很重要的东西,让我和那些至少看起来过的很不错的人之间有明显的差异,一眼可以分别出来。
我还是想再找找看,看下在未来几年能不能找到。
这可能是我以后追寻的一个方向。
希望未来对我好点。

假如有一个方法一直在打印提示信息,现在想要在main方法中经过一段的时间主动的去停止,这个时候我们可以选择使用共享变量的方式(注意加读写锁),也可以使用channel的方式。例如:

共享变量:

var stop bool
var rwlock sync.RWMutex
var wg sync.WaitGroup
func g1() {
    for {
        rwlock.RLock()
        if stop {
            fmt.Println("退出了捏")
            wg.Done()
            return
        }
        time.Sleep(time.Second)
        fmt.Println("欸嘿!")
        rwlock.RUnlock()
    }
}

func main() {
    wg.Add(1)
    go g1()
    
    time.Sleep(3*time.Second)
    rwlock.Lock()
    stop = true
    rwlock.Unlock()
    wg.Wait()
    fmt.Println("退出程序")
}

运行结果:

欸嘿!
欸嘿!
欸嘿!
退出了捏
退出程序

使用channel:

var wg sync.WaitGroup

func g1(stop chan struct{}) {
    for {
        select {
        case <- stop:
            fmt.Println("退出了捏")
            wg.Done()
            return
        default:
            time.Sleep(time.Second)
            fmt.Println("欸嘿!")
        }
    }
}

func main() {
    stop := make(chan struct{})
    wg.Add(1)
    go g1(stop)
    time.Sleep(3*time.Second)
    stop <- struct{}{}
    wg.Wait()
    fmt.Println("退出程序")
}
因为channel是协程安全的所以不用加锁

运行结果:

欸嘿!
欸嘿!
欸嘿!
退出了捏
退出程序

除此之外,还有一种更常用的方式,就是使用context,例如:

var wg sync.WaitGroup

func g1(ctx context.Context) {
    for {
        select {
        case <- ctx.Done():
            fmt.Println("退出了捏")
            wg.Done()
            return
        default:
            time.Sleep(time.Second)
            fmt.Println("欸嘿!")
        }
    }
}

func main() {
    wg.Add(1)
    ctx, cancel := context.WithCancel(context.Background())
    go g1(ctx)
    time.Sleep(3*time.Second)
    cancel()
    wg.Wait()
    fmt.Println("退出程序")
}

运行结果:

欸嘿!
欸嘿!
欸嘿!
退出了捏
退出程序
WithCancel() 方法返回值是一个 context.Context 类型的interface和一个cancel方法
ctx(context.Context)有一个Done方法,返回值是一个channel
当使用cancel方法之后,ctx会向Done()返回的channel中发送一个信号
此时接受到信号,select跳转到退出的分支,由此完成对goroutine的信息传递实现主动退出

假如现在有两个方法,分别有对应的channel,每当他们运行完成之后会向channel中发送一个信号。现在想要知道是哪一个方法先完成,可以如何做?

此时可以使用select方法,来对channel进行监控。

select的语法和switch很像,例如:

func g1(ch chan struct{}) {
    time.Sleep(time.Second)
    ch <- struct{}{}
}

func g2(ch chan struct{}) {
    time.Sleep(2*time.Second)
    ch <- struct{}{}
}

func main() {
    ch1 := make(chan struct{})
    ch2 := make(chan struct{})

    go g1(ch1)
    go g2(ch2)
    select {
    case <-ch1:
        fmt.Println("g1 done")
    case <-ch2:
        fmt.Println("g2 done")
    }
}

运行结果为:

g1 done

假如我现在将这个select放到一个for循环中,不断地监控两个channel,并且想要当超过一定的时间之后就自动退出,也就是设定超时时间,这个时候可以使用 time.NewTimer() 这个方法接收一个时间做为参数,并返回一个timer对象,这个对象有一个成员 C 是一个channel,当时间到达时timer会往这个timer中发送一个信号。我们可以根据这个原理来完成超时时间的设置比如:

func g1(ch chan struct{}) {
    time.Sleep(time.Second)
    ch <- struct{}{}
}

func g2(ch chan struct{}) {
    time.Sleep(2*time.Second)
    ch <- struct{}{}
}

func main() {
    ch1 := make(chan struct{})
    ch2 := make(chan struct{})
    timer := time.NewTimer(4*time.Second)

    go g1(ch1)
    go g2(ch2)
    for {
        select {
        case <-ch1:
            fmt.Println("g1 done")
        case <-ch2:
            fmt.Println("g2 done")
        case <-timer.C:
            fmt.Println("time out!")
            return
        }
    }
}

运行结果如下:

g1 done
g2 done
time out!

同时和switch一样,select也支持default方法,当没有读取到值时就运行default。

关于select还有两点需要注意的:

  1. select时某一个分支就绪了(获取到了值)就执行哪一个分支
  2. 如果多个分支同时就绪,是随机执行,目的是为了防止一直读取某一个分支的值,从而造成”饥饿“现象

channel如果直接定义一般都是双向的,即我们既可以往里面写值,也可以从里面读值。

但是有的时候我们希望某一个方法里面只能是读值,不能写值、或者是只能够写值,不能够读值,此时就需要用到单项channel。

单向channel的定义和双向channel差不多,只是多了一个 <- 来标识是单向写入还是单向取出

var ch1 chan<- int // 写入
var ch2 <-chan int // 取出

对于初始化可以通过将一个双向channel赋值给单向channel来完成初始化,go会在内部自动完成类型的转换。

var ch1 chan<- int // 写入
var ch2 <-chan int // 取出

ch := make(chan int, 1)
ch1 = ch
ch2 = ch

例如:

var wg sync.WaitGroup
func producer(out chan<- int) {
    defer wg.Done()
    for i:= 0; i < 10; i ++ {
        out <- i
    }
    close(out)
}

func receiver(in <-chan int) {
    defer wg.Done()
    for num := range in {
        fmt.Println(num)
    }
}

func main() {
    wg.Add(2)
    c := make(chan int)
    go producer(c)
    go receiver(c)
    wg.Wait()
}

运行结果如下:

0
1
2
3
4
5
6
7
8
9