Home > Software design >  Synchronising writes to io.MultiWriter in golang
Synchronising writes to io.MultiWriter in golang

Time:10-09

I am working on an app that should be able to write to both a bytes.Buffer as well to os.Stdout / os.Stderr.

Therefore I am creating an

w := io.MultiWriter(myBuf, os.Stdout)

The writes will be from multiple goroutines.

To make at least my bytes.Buffer thread safe, I am wrapping it

type Buffer struct {
    buffer bytes.Buffer
    mutex  sync.Mutex
}

func (s *Buffer) Write(p []byte) (n int, err error) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.buffer.Write(p)
}

func (s *Buffer) String() string {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.buffer.String()
}

How can I achieve the same result with the standard error / output writes?

I thought about using log but it does not implement the io.Writer interface.

CodePudding user response:

How can I achieve the same result with the standard error / output writes?

With a mutex, like you said.

I thought about using log but it does not implement the io.Writer interface.

Interesting idea, since it locks itself, but you can do this with plain old os.Stdout and os.Stderr, both of which implement io.Writer:

package main

import (
    "fmt"
    "io"
    "os"
    "sync"
)

type LockedWriter struct {
    w io.Writer
    l sync.Mutex
}

func (lw *LockedWriter) Write(p []byte) (n int, err error) {
    lw.l.Lock()
    defer lw.l.Unlock()
    return lw.w.Write(p)
}

func main() {
    var wg sync.WaitGroup
    var w = &LockedWriter{
        w: io.MultiWriter(os.Stdout, os.Stderr),
    }
    for i := 0; i < 10; i   {
        wg.Add(1)
        go func(i int) {
            for j := 0; j < i; j   {
                fmt.Fprintf(w, "I am goroutine %d (%d/%d)\n", i, j, i)
            }
            wg.Done()
        }(i)
    }
    wg.Wait()
}

In this particular case, I couldn't reproduce any interpolated writes, but I think if the messages were long enough or the goroutines were doing more operations, I would.

CodePudding user response:

Will this help:

type StdoutType {
    stdout *File
    mutex sync.Mutex
}

func NewStdoutType() *StdoutType {
    return &StdoutType{
        stdout: os.Stdout,
    }
}

func (s *StdoutType) Write(p []byte) (n int, err error) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.stdout.Write(p)
}

StdoutType is now io.Writer compatible

  •  Tags:  
  • go
  • Related