Контекстный срок не отменяет рутинную функцию, если я не сократил срок

Я модифицировал функцию из книги, которую читал: эта функция отправляет писателю «пинг» через заданный интервал. Программа выполняется в режиме go, поэтому я добавил контекст, чтобы функция могла вернуться после истечения крайнего срока контекста. Однако функция не вернется, если я не установлю начальный интервал равным 4 секундам или меньше.

package pinge

import (
    "context"
    "fmt"
    "io"
    "time"
)

const defaultInterval = time.Second * 15
func Pinger(ctx context.Context, w io.Writer, durChan <-chan time.Duration) (count int, err error) {
    interval := defaultInterval
    count = 0

    select {
    case <-ctx.Done():
        return count, ctx.Err()
    case interval = <-durChan:
        if interval <= 0 {
            interval = defaultInterval
        }
    default:
    }

    t := time.NewTimer(interval)
    defer func() {
        if !t.Stop() {
            <-t.C
        }
    }()

    for {
        select {
        case <-ctx.Done():
            fmt.Println("Deadline exceeded")
            return count, ctx.Err()
        case newInterval := <-durChan:
            if newInterval > 0 {
                interval = newInterval
            }
            if !t.Stop() {
                <-t.C
            }
        case <-t.C:
            if _, err := w.Write([]byte("ping")); err != nil {
                return count, err
            }
            count++
        }
        t.Reset(interval)
    }
}

Я написал тестовый сценарий для проверки этой функции, но время ожидания продолжает истекать, поскольку тест не вернулся в течение 30-секундного периода ожидания. Вот моя тестовая функция

func TestPinger(t *testing.T) {

    ddl := time.Now().Add(time.Second * 10)
    initInterval := time.Second * 2
    countChan := make(chan int)
    durChan := make(chan time.Duration, 1)
    doneChan := make(chan struct{})


    ctx, cancelCtx := context.WithDeadline(context.Background(), ddl)
    defer cancelCtx()

    r, w := io.Pipe()

    durChan <- initInterval

    go func() {
        count, err := Pinger(ctx, w, durChan)
        countChan <- count
        if err != nil {
            doneChan <- struct{}{}
        }
    }()

    buf := make([]byte, 1024)

    n, err := r.Read(buf)
    if err != nil {
        t.Error("Could not read buffer: ", err)
    }

    fmt.Printf("Received: %q\n", buf[:n])

    var pingCount int

    select {
    case <- doneChan: 
        fmt.Println("Ping Count  = ", pingCount)
        return
    case pingCount = <-countChan:
    }
    fmt.Println("Ping Count  = ", pingCount)
}
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
0
0
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Причина зависания кода заключается в том, что вы читаете из канала только один раз (т. е. первую запись). В документации к трубе написано:

Операции чтения и записи в канале сопоставляются один к одному, за исключением случаев, когда для использования одной записи требуется несколько операций чтения. То есть каждая запись в PipeWriter блокируется до тех пор, пока не будет удовлетворена одна или несколько операций чтения из PipeReader, которые полностью потребляют записанные данные.

Итак, в коде функция TestPinger будет блокироваться в режиме онлайн.

n, err := r.Read(buf)

до тех пор, пока не сработает таймер в Pinger и w.Write([]byte("ping")), это приведет к возврату функции Read, а затем к блокировке оператора select. Однако в Pinger, когда таймер срабатывает снова, он теперь застревает в w.Write, поскольку никто не читает из канала... поэтому цикл застревает там и, следовательно, не имеет возможности увидеть, что срок действия контекста истек. .

Исправить: Отправьте WriteCloser в Pinger и закройте w при выходе:

func Pinger(ctx context.Context, w io.WriteCloser, durChan <-chan time.Duration) (count int, err error) {
    defer w.Close()

и в основной функции читайте до EOF, т.е.:

    buf := bytes.NewBuffer(nil)

    _, err := io.Copy(buf, r)
    if err != nil {
        t.Error("Could not read buffer: ", err)
    }

    fmt.Printf("Received: %q\n", buf.String())

Это сработало как по волшебству. Спасибо @ain

Dassy Areg 25.06.2024 17:36

Другие вопросы по теме