Go 如何使阻塞的外部库调用超时?

Go 如何使阻塞的外部库调用超时?,go,concurrency,timeout,goroutine,Go,Concurrency,Timeout,Goroutine,(我不相信我的问题与这个QA:是重复的,因为我正在运行Go 1.9,它有先发制人的调度程序,而这个问题是针对Go 1.2提出的) 我的Go程序调用另一个Go-lang库包装的C库,该库发出一个阻塞调用,持续时间超过60秒。我想添加一个超时,使其在3秒内返回: 带有长块的旧代码: // InvokeSomething is part of a Go wrapper library that calls the C library read_something function. I cannot

(我不相信我的问题与这个QA:是重复的,因为我正在运行Go 1.9,它有先发制人的调度程序,而这个问题是针对Go 1.2提出的)

我的Go程序调用另一个Go-lang库包装的C库,该库发出一个阻塞调用,持续时间超过60秒。我想添加一个超时,使其在3秒内返回:

带有长块的旧代码:

// InvokeSomething is part of a Go wrapper library that calls the C library read_something function. I cannot change this code.

func InvokeSomething() ([]Something, error)  {
    ret := clib.read_something(&input) // this can block for 60 seconds
    if ret.Code > 1 {
        return nil, CreateError(ret)
    }
    return ret.Something, nil
}

// This is my code I can change:

func MyCode() {

    something, err := InvokeSomething()
    // etc
}
我的代码带有go例程、通道和超时,基于以下go示例:


…但是,它没有任何帮助,仍然会阻塞60秒。

您提出的在goroutine中执行线程锁定的解决方案始于
mycodeithtimeout()
并不能保证
mycodeithtimeout()
将在3秒后返回,这样做的原因是:第一:不能保证启动的goroutine会得到调度并达到将线程锁定到goroutine的点;第二:因为即使调用外部命令或syscall并在3秒内返回,也不能保证运行的另一个goroutine
mycodeithtimeout()
将被安排接收结果

而是在
mycodeithtimeout()
中执行线程锁定,而不是在它启动的goroutine中:

func MyCodeWithTimeout() {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()

    ch = make(chan somethingResult, 1);
    defer close(ch)
    go func() {
        something, err := InvokeSomething() // blocks here for 60 seconds
        ret := somethingResult{ something, err }
        ch <- ret
    }()
    select {
    case result := <-ch:
        // etc
    case <-time.After(time.Second *3):
        // report timeout
    }
}
请注意,在这个循环中使用的“冲动”会使保证失效,因为
time.Sleep()
可能会在其实现中使用goroutines,并且肯定不会保证在给定的持续时间之后完全返回


另外请注意,如果您有8个CPU内核,并且返回8个内核,并且您的goroutines仍然“饥饿”,这可能是一个临时解决方案,但是您在应用程序中使用Go的并发原语(或者没有使用它们)时仍然存在更严重的问题,您应该调查并“修复”这些问题。将线程锁定到goroutine甚至可能会使其他goroutine的情况更糟。

go1.9版的“notreplicate”代码仍然会在playway中阻塞。我怀疑这到底是同一个原因。你正在设置GOMAXPROCS吗?@superfell项目中的其他代码集
GOMAXPROCS(runtime.numpu())
在我的机器上是
8
。所以它应该仍然可以工作…在mycodeithtimeout()中执行线程锁定,而不是在它启动的goroutine中。
func MyCodeWithTimeout() {
    ch = make(chan somethingResult, 1);
    defer close(ch)
    go func() {
        runtime.LockOSThread()
        defer runtime.UnlockOSThread()
        something, err := InvokeSomething() // blocks here for 60 seconds
        ret := somethingResult{ something, err }
        ch <- ret
    }()
    select {
    case result := <-ch:
        // etc
    case <-time.After(time.Second *3):
        // report timeout
    }
}
func MyCodeWithTimeout() {
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()

    ch = make(chan somethingResult, 1);
    defer close(ch)
    go func() {
        something, err := InvokeSomething() // blocks here for 60 seconds
        ret := somethingResult{ something, err }
        ch <- ret
    }()
    select {
    case result := <-ch:
        // etc
    case <-time.After(time.Second *3):
        // report timeout
    }
}
for end := time.Now().Add(time.Second * 3); time.Now().Before(end); {
    // Do non-blocking check:
    select {
    case result := <-ch:
        // Process result
    default: // Must have default to be non-blocking
    }
}