V2EX xuantedev 的所有回复 第 1 页 / 共 1 页
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX    xuantedev    全部回复第 1 页 / 共 1 页
回复总数  16
2021-08-25 15:31:41 +08:00
回复了 xuantedev 创建的主题 git 一直很困惑 bitbucket 和 github 的 repo 路径组织方式
@2i2Re2PLMaDnghL 如果是这样的话,一个 repo 对应一个 project,那 bitbucket 应该不用提出 project 这个概念了啊。
2021-08-25 15:00:26 +08:00
回复了 xuantedev 创建的主题 git 一直很困惑 bitbucket 和 github 的 repo 路径组织方式
@2i2Re2PLMaDnghL 谢谢,为什么我要从一个路径来判断这是一个 project 还是一个 repo 呢?绝大多数 project 只有一个 repo,我本身不是很赞同这个说法。何况这只是一个组织方式,一个 project 中只有一个代码库也许情况比较多,但是 document 呢?

@wangkun025 谢谢,但是我还是不理解我这个问题跟 auth token 有什么关系。
@lujjjh 太感谢了,这应该是正确的做法了。前面看了很多代码片段,都是把 Stop()放在 select 之外的,头脑禁锢了。感谢感谢~
@lujjjh 感谢细心回复。

现在的问题是,在消费 channel 中的内容的时候,您是选择阻塞消耗,还是尝试消耗,这两者只能选择一个。

阻塞消耗,如果 f 没有调用,就会阻塞:
```go
select {
case <-timer.C:
xxx
}
```
尝试性消耗,即使 f 没有调用,也不会阻塞:
``go
select {
case <-timer.C
xxxx
default:
xxxx
```

如果选择阻塞性消耗,就是您编写的代码情况,那么假定的前题条件是:Stop()返回 False 表明一定执行了 f 。但是在某些情况下,不能做这样的保证,可以参考这个 bug:
```
https://github.com/golang/go/issues/14038
```

如果 Stop()返回了 False,但是没有执行 f,那么上面的阻塞式读取就会出现问题。我们再看看什么场景下会出现 Stop()返回 false,但是 f 没有执行的情况。

只要前面超时过(第一次发送了 channel 数据),然后您的程序从正常逻辑(不是 drain channel 那段)中读取了数据,Channel 会为空。此时您去 Stop(),肯定返回 False,您再去阻塞读取 Channel,那么就会阻塞。

您可以拿代码运行一下看看:第一个匿名函数是生产者,第二个匿名函数是正常的业务逻辑(即消费者)。
正常情况下,读取数据的超时值是 2 秒。
第一次读取就超时,因为生产者 3 秒后才生产数据,超时后,BBB 处的代码会把 channel 读取干净,这也很正常,超时事件发送给我们,我们消耗它。
循环第二次执行,Stop()会返回 False (因为定时器已经 expired 了),但是不会调用 f 去 sendTime,这也就是我前面说过的超时和 f 的调用是两件独立的事情,Stop()的返回值不会反应是否发送 sendTime 。
此时,再 AAA 处的阻塞调用,就会阻塞。因为 Channel 里面的确没有数据。

```go
package main

import (
"fmt"
"time"
)

func main() {
c := make(chan bool)
go func() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second * 3)
c <- false
}
}()

go func() {
t := time.NewTimer(time.Second * 2)
for {
if !t.Stop() {
<-t.C // AAA: will blocked here
}
t.Reset(time.Second * 2)
select {
case b := <-c:
if !b {
fmt.Println(time.Now(), "work...")
continue
}
case <-t.C: // BBB: normal receive from channel timeout event
fmt.Println(time.Now(), "timeout")
continue
}
}
}()

time.Sleep(time.Second * 10000000)
}
```

如果改成非阻塞性读取,那么又有我第一次说的那个多了 f 调用的问题。
@lujjjh 感谢兄弟的细心阅读,并且一眼就看出了我的担心,握爪。是这样的,其实问题就消费 Channel 这里。
有一种极端的情况,就是我消费 Channel 的时候,runtime 的 deltimer()还没有来得及调用 sendTime 。但是当我的消费代码走完了以后,它调用了 sendTime,导致了 Channel 里面又有了内容,这样,我下次正儿八经的业务 select 的时候,立马就会返回,导致我错误的认为超时了。

这个问题的出在 Runtime 中 timer 的维护 routine 中调用定时器处理函数 runOneTimer()。该函数的处理分为两部分:
* A) 从 timer 堆中处理定时器状态这部分
* B) 调用 f(arg, seq)来 sentTime 到 Channel 中
从 [代码]( https://github.com/golang/go/blob/891547e2d4bc2a23973e2c9f972ce69b2b48478e/src/runtime/time.go#L818)看,这两个部分操作没有放在一个锁中,因此不是原子操作。
```go
func runOneTimer(pp *p, t *timer, now int64) {
.......
if raceenabled {
// Temporarily use the current P's racectx for g0.
gp := getg()
if gp.racectx != 0 {
throw("runOneTimer: unexpected racectx")
}
gp.racectx = gp.m.p.ptr().timerRaceCtx
}

unlock(&pp.timersLock)

f(arg, seq) // 就是那个 sendTime 函数

lock(&pp.timersLock)

.......
}
```

设想在用户调用 Stop()的时候,虽然和 runOneTimer()的 A 部分产生了互斥,但是和 sentTime 并没有产生互斥。极端情况下,如果 runtime 的 routine 中把 A 部分执行完了,碰到 CPU 繁忙或者系统调度等极端场景,B 部分一直没有机会执行。那么 User 的 Stop()、消费 Channel (实际上没有数据)、然后 Reset(),到这里 B 部分都没有执行。

这样,Channel 中一直没有数据,但是当用户调用业务 Select()的时候,runtime routine 中的 B 部分执行了,sendTime 将数据发送到了 Channel 中。select()就因为上一次 Timer 的数据而触发了。

这也就是我在问题中说的,`(理论上)一定概率的立即超时`。


其实 Golang 的相关模块作者也注意到了该问题,修改的次数非常多,提供的注释也非常长。


其中作者提到,Stop()并不会等到 f (即 sentTime)执行完毕后才返回,也就是说 Stop()只反馈 Timer 当时的状态,不反馈 channel 的状态。
```
// Stop prevents the Timer from firing.
// It returns true if the call stops the timer, false if the timer has already
// expired or been stopped.
// Stop does not close the channel, to prevent a read from the channel succeeding
// incorrectly.
//
// To ensure the channel is empty after a call to Stop, check the
// return value and drain the channel.
// For example, assuming the program has not received from t.C already:
//
// if !t.Stop() {
// <-t.C
// }
//
// This cannot be done concurrent to other receives from the Timer's
// channel or other calls to the Timer's Stop method.
//
// For a timer created with AfterFunc(d, f), if t.Stop returns false, then the timer
// has already expired and the function f has been started in its own goroutine;
// Stop does not wait for f to complete before returning.
// If the caller needs to know whether f is completed, it must coordinate
// with f explicitly.
```

而对于 Reset(),作者也是用了大量篇幅,指出 Reset 的返回值并不是准确的,因为竟态导致的问题。提供返回值只是为了兼容过去的接口。另外其实 time 模块的 Reset()调用的其实就是 runtime 的 resetTimer,而 resetTimer()其实就是内部调用 stopTimer()。
```
// Reset changes the timer to expire after duration d.
// It returns true if the timer had been active, false if the timer had
// expired or been stopped.
//
// For a Timer created with NewTimer, Reset should be invoked only on
// stopped or expired timers with drained channels.
//
// If a program has already received a value from t.C, the timer is known
// to have expired and the channel drained, so t.Reset can be used directly.
// If a program has not yet received a value from t.C, however,
// the timer must be stopped andif Stop reports that the timer expired
// before being stoppedthe channel explicitly drained:
//
// if !t.Stop() {
// <-t.C
// }
// t.Reset(d)
//
// This should not be done concurrent to other receives from the Timer's
// channel.
//
// Note that it is not possible to use Reset's return value correctly, as there
// is a race condition between draining the channel and the new timer expiring.
// Reset should always be invoked on stopped or expired channels, as described above.
// The return value exists to preserve compatibility with existing programs.
//
// For a Timer created with AfterFunc(d, f), Reset either reschedules
// when f will run, in which case Reset returns true, or schedules f
// to run again, in which case it returns false.
// When Reset returns false, Reset neither waits for the prior f to
// complete before returning nor does it guarantee that the subsequent
// goroutine running f does not run concurrently with the prior
// one. If the caller needs to know whether the prior execution of
// f is completed, it must coordinate with f explicitly.
```

所以从这些场景看,很难精确的判断出来 Channel 是否有数据,而尝试去 Drain Channel 也只是大概率可以 drain out,极端情况下,drain empty channel 之后,还会立即有数据 send 进来。
@lesismal 感谢大神赐教,受匪浅。
2020-12-22 09:09:27 +08:00
回复了 xuantedev 创建的主题 Android 安利一个 Android(AOSP)的源代码交叉索引
@richard1122 界面的区别哈,这个用 opengrok 的界面,列出来的条目比较清晰,可以指定子工程和目录,看不同的习惯,好多老同学喜欢这个:)
能看源代码的都是一堆事情要去做的主,视频这种信息密度太低的媒体不适合他们,偶尔看一下也许有可能,但是很难形成习惯。休闲的、打法时间的视频才吸引人。
@WebKit 100 多电费楼主的机器也可以用很久吧。
2020-10-28 20:53:23 +08:00
回复了 xuantedev 创建的主题 git 感觉最近几个月来大的 git 库很难下载成功了
我一般从 tuna mirror 上下载比较大的 git 库。
2020-10-28 20:52:19 +08:00
回复了 xuantedev 创建的主题 git 感觉最近几个月来大的 git 库很难下载成功了
谢谢大家,我这儿是指 git 库,不一定是 google source 或者说 github 。不是访问不了,其实我觉得问题还是出在 https 上,不让太长时间处于连接。
@mangogeek 谢谢,我主要是想看看比如某个平台,比如 Qualcomm 平台的 msm 系列、或者飞思卡尔的实现大概在哪些文件中,然后参考下。
2020-10-19 17:18:45 +08:00
回复了 xuantedev 创建的主题 Android 一直墙内可以用的 g.cn/generate_204 终于挂了
今天又可以用了,谢谢大家的帮助~
2020-10-19 17:18:10 +08:00
回复了 wvtjplh 创建的主题 分享创造 写了一个电子发票合并打印的工具,请大家捧场
@howellz 是的,最初用这个名字的是这个网站 https://fapiao.bangnimang.net ,后来楼主也做了个类似的。其实再做一个本来没啥,关键是用别家名字就不厚道了。
2020-10-18 16:08:16 +08:00
回复了 wvtjplh 创建的主题 分享创造 写了一个电子发票合并打印的工具,请大家捧场
@Muniesa 呵呵,楼主做个这个不错,但是连人家的名字都抄袭,就有点不厚道了。
2020-10-18 16:07:02 +08:00
回复了 wvtjplh 创建的主题 分享创造 写了一个电子发票合并打印的工具,请大家捧场
楼主,虽然做一个这样的工具每个人都可以做,但是把人家的名字直接拿到自己的主页作关键词就有点不厚道了。
这里都是搞技术的,你做得好,大家都会捧场,但是这种直接挂人家羊头的方式太不值得。
建议把主页的标题中别人的作品名去掉。
这里是人家的作品名字:
https://img-blog.csdnimg.cn/20190916161941563.png
< href="https://www.digitalocean.com/?refcode=1b51f1a7651d" target="_blank">
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2331 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 23ms UTC 15:48 PVG 23:48 LAX 07:48 JFK 10:48
Do have faith in what you're doing.
ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86