如何在IO上设置带超时的读取

huangapple go评论62阅读模式
英文:

How to do a read with timeout on IO

问题

以下是您要翻译的代码部分:

function read_with_timeout(io::IO, timeout::Int)
    read(io, UInt16)
end

function read_with_timeout(io::IO, type::Type, timeout::Int)
    sleepTime = 0.01
    totalTime = 0
    value = nothing
    @async value = read(io, type)
    while value === nothing
        if totalTime >= timeout
            throw(TimeoutException())
        end
        sleep(sleepTime)
        totalTime += sleepTime
    end
    value
end
英文:

How do I add a timeout for reading from an IO in julia?

function read_with_timeout(io::IO, timeout::Int)
    read(io, UInt16)
end

Is this a good impl?

function read_with_timeout(io::IO, type::Type, timeout::Int)
    sleepTime = 0.01
    totalTime = 0
    value = nothing
    @async value = read(io, type)
    while value === nothing
        if totalTime >= timeout
            throw(TimeoutException())
        end
        sleep(sleepTime)
        totalTime += sleepTime
    end
    value
end

答案1

得分: 2

这种轮询任务的一般思路绝对是有效的解决方案。如果你测量实际触发超时所需的时间,你会发现它比请求的超时时间长。它总是会稍微长一些,但通过测量已经过去的时间而不是假设sleepTime已经过去,我们可以更准确。

你的函数与Base.timedwait非常相似,它在测量方面更加谨慎。以下是带有timedwait的函数版本:

function read_with_timeout(io, type, timeout)
    task = @async read(io, type)
    status = timedwait(timeout) do
        istaskdone(task)
    end
    if status == :ok
        fetch(task)
    else
        throw(TimeoutException())
    end
end

这个函数在这个实现和你的实现中都存在一个更严重的问题。已启动的任务仍然在等待数据,并且会默默地消耗任何可用的数据。

一个更好的解决方案可能可以通过使用更低级别的读取函数找到,但我们也可以中断任务(要注意竞争条件):

function read_with_timeout(io, type, timeout)
    task = @async read(io, type)
    status = timedwait(timeout) do
        istaskdone(task)
    end
    if status == :ok
        fetch(task)
    else
        @async Base.throwto(task, TimeoutException())
        fetch(task)
    end
end
英文:

The general idea of polling the task this way is definitely a valid solution. If you measure the actual time it takes for the timeout to trigger in your implementation you'll see that it takes longer thsn the requested timeout. It will always be slightly longer but we can be more accurrate by measuring the time that has passed instead of assuming that sleepTime had passed.

Your function is very similar to Base.timedwait which does the measuring a bit more carefully. Here is a version of the function with timedwait:

function read_with_timeout(io, type, timeout)
    task = @async read(io, type)
    status = timedwait(timeout) do
        istaskdone(task)
    end
    if status == :ok
        fetch(task)
    else
        throw(TimeoutException())
    end
end

There is a more serious issue with this function in both this implementation and your implementation. The started task is still around waiting for data and will silently consume any available data.

A nicer solution could probably be found by reaching for lower level reading functions but we can also interrupt the task (be mindful of race conditions):

function read_with_timeout(io, type, timeout)
    task = @async read(io, type)
    status = timedwait(timeout) do
        istaskdone(task)
    end
    if status == :ok
        fetch(task)
    else
        @async Base.throwto(task, TimeoutException())
        fetch(task)
    end
end

huangapple
  • 本文由 发表于 2023年6月30日 01:25:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76583327.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定