如何确保函数在Go中占用一定的时间?

Ter*_*way 5 python time go sqrl

我正在Go中为SQRL客户端实施EnScrypt.该函数需要运行,直到它使用最少的CPU时间.我的Python代码如下所示:

def enscrypt_time(salt, password, seconds, n=9, r=256):
    N = 1 << n
    start = time.process_time()
    end = start + seconds
    data = acc = scrypt.hash(password, salt, N, r, 1, 32)
    i = 1
    while time.process_time() < end:
        data = scrypt.hash(password, data, N, r, 1, 32)
        acc = xor_bytes(acc, data)
        i += 1
    return i, time.process_time() - start, acc
Run Code Online (Sandbox Code Playgroud)

除了process_time函数之外,将其转换为Go非常简单.我不能使用time.Time/ Timer因为那些测量挂钟时间(受到系统上可能运行的其他所有内容的影响).我需要实际使用的CPU时间,理想情况是函数,或者至少需要运行的线程或进程.

什么是Go相当于process_time

https://docs.python.org/3/library/time.html#time.process_time

icz*_*cza 2

您可以使用runtime.LockOSThread()将调用 goroutine 连接到其当前的操作系统线程。这将确保没有其他 goroutine 被调度到该线程,因此您的 goroutine 将运行并且不会被中断或搁置。当线程被锁定时,其他 goroutine 不会干扰。

之后,您只需要一个循环,直到给定的秒数过去。您必须调用runtime.UnlockOSThread()“释放”线程并使其可供其他 goroutine 执行,最好作为defer语句来完成。

看这个例子:

func runUntil(end time.Time) {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()
    for time.Now().Before(end) {
    }
}
Run Code Online (Sandbox Code Playgroud)

要让它等待 2 秒,它可能如下所示:

start := time.Now()
end := start.Add(time.Second * 2)
runUntil(end)

fmt.Println("Verify:", time.Now().Sub(start))
Run Code Online (Sandbox Code Playgroud)

例如,这会打印:

Verify: 2.0004556s
Run Code Online (Sandbox Code Playgroud)

当然你也可以指定少于一秒,例如等待 100 毫秒:

start := time.Now()
runUntil(start.Add(time.Millisecond * 100))
fmt.Println("Verify:", time.Now().Sub(start))
Run Code Online (Sandbox Code Playgroud)

输出:

Verify: 100.1278ms
Run Code Online (Sandbox Code Playgroud)

如果更适合您,您可以使用此函数的不同版本,该版本将“等待”时间量设为time.Duration

func wait(d time.Duration) {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()

    for end := time.Now().Add(d); time.Now().Before(end); {
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这个:

start = time.Now()
wait(time.Millisecond * 200)
fmt.Println("Verify:", time.Now().Sub(start))
Run Code Online (Sandbox Code Playgroud)

输出:

Verify: 200.1546ms
Run Code Online (Sandbox Code Playgroud)

注意:请注意,上述函数中的循环将无情地使用 CPU,因为其中没有睡眠或阻塞 IO,它们只会查询当前系统时间并将其与截止时间进行比较。

如果攻击者通过多次并发尝试增加系统负载怎么办?

Go运行时限制了可以同时执行goroutines的系统线程。这是由 控制的runtime.GOMAXPROCS(),所以这已经是一个限制了。它默认为可用 CPU 核心数,您可以随时更改它。不过,这也造成了瓶颈,因为通过使用runtime.LockOSThread(),如果锁定线程的数量在任何给定时间等于GOMAXPROCS,那么就会阻塞其他 goroutine 的执行,直到线程被解锁。

查看相关问题:

Go运行时使用的线程数

为什么golang写文件时很多goroutine被阻塞却没有创建很多线程?