How do the io.Pipe Write() and Read() functions work?

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

How do the io.Pipe Write() and Read() functions work?

问题

通过阅读golang的源代码pipe.go,我想弄清楚管道(pipe)是如何工作的,然后遇到了这两个write()和read()函数。让我困惑的是,如果读取器调用read()函数并持有l.lock,然后等待数据,那么写入器如何调用write()函数并获取l.lock来写入数据呢?

func (p *pipe) write(b []byte) (n int, err error) {
    // pipe uses nil to mean not available
    if b == nil {
        b = zero[:]
    }

    // One writer at a time.
    p.wl.Lock()
    defer p.wl.Unlock()

    p.l.Lock()
    defer p.l.Unlock()
    if p.werr != nil {
        err = ErrClosedPipe
        return
    }
    p.data = b
    p.rwait.Signal()
    for {
        if p.data == nil {
            break
        }
        if p.rerr != nil {
            err = p.rerr
            break
        }
        if p.werr != nil {
            err = ErrClosedPipe
            break
        }
        p.wwait.Wait()
    }
    n = len(b) - len(p.data)
    p.data = nil // in case of rerr or werr
    return
}

and read:

func (p *pipe) read(b []byte) (n int, err error) {

    // One reader at a time.
    p.rl.Lock()
    defer p.rl.Unlock()

    p.l.Lock()
    defer p.l.Unlock()
    for {
        if p.rerr != nil {
            return 0, ErrClosedPipe
        }
        if p.data != nil {
            break
        }
        if p.werr != nil {
            return 0, p.werr
        }
        p.rwait.Wait()
    }
    n = copy(b, p.data)
    p.data = p.data[n:]
    if len(p.data) == 0 {
        p.data = nil
        p.wwait.Signal()
    }
    return
}

这段代码中,通过使用锁来实现一次只能有一个写入器或读取器访问管道。在写入器的write()函数中,首先获取写入锁p.wl,然后获取管道锁p.l。如果发生错误或管道已关闭,会返回相应的错误。然后将数据写入管道,并通过p.rwait.Signal()通知读取器有数据可用。之后,通过循环等待读取器读取数据,直到数据被读取完或发生错误。

在读取器的read()函数中,首先获取读取锁p.rl,然后获取管道锁p.l。通过循环等待数据可用,直到数据被写入或发生错误。然后将数据从管道中复制到给定的缓冲区b中,并更新管道中的数据。如果数据已经全部读取完,则通过p.wwait.Signal()通知写入器可以继续写入数据。

这样通过使用锁和条件变量,实现了管道的读写同步和互斥访问。

英文:

By reading golang src pipe.go to figure out how pipe works, I ran into these two write() & read() functions. What confuses me is that if reader calls read() func and hold l.lock and then waiting for data, how does writer call write() func and acquire l.lock to write data in?

func (p *pipe) write(b []byte) (n int, err error) {
// pipe uses nil to mean not available
if b == nil {
b = zero[:]
}
// One writer at a time.
p.wl.Lock()
defer p.wl.Unlock()
p.l.Lock()
defer p.l.Unlock()
if p.werr != nil {
err = ErrClosedPipe
return
}
p.data = b
p.rwait.Signal()
for {
if p.data == nil {
break
}
if p.rerr != nil {
err = p.rerr
break
}
if p.werr != nil {
err = ErrClosedPipe
break
}
p.wwait.Wait()
}
n = len(b) - len(p.data)
p.data = nil // in case of rerr or werr
return
}

and read:

func (p *pipe) read(b []byte) (n int, err error) {
// One reader at a time.
p.rl.Lock()
defer p.rl.Unlock()
p.l.Lock()
defer p.l.Unlock()
for {
if p.rerr != nil {
return 0, ErrClosedPipe
}
if p.data != nil {
break
}
if p.werr != nil {
return 0, p.werr
}
p.rwait.Wait()
}
n = copy(b, p.data)
p.data = p.data[n:]
if len(p.data) == 0 {
p.data = nil
p.wwait.Signal()
}
return
}

<!-- end snippet -->

答案1

得分: 2

互斥锁p.l在读取和写入sync.Cond条件中使用,根据需要进行锁定和解锁。

在条件上调用Wait会解锁其锁,并等待相应的Signal调用。你可以看到管道在ReadWrite方法中使用p.wwaitp.rwait来协调读取器和写入器。

英文:

The mutex p.l is used in the read and write sync.Cond conditions, which will lock and unlock it as necessary.

Calling Wait on the condition unlocks its lock, and waits for a corresponding Signal call. You can see that the pipe uses p.wwait and p.rwait to coordinate the readers and writers within the Read and Write methods.

huangapple
  • 本文由 发表于 2017年8月31日 23:52:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/45985575.html
匿名

发表评论

匿名网友

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

确定