SerialPort SerialStream утечка памяти в цикле чтения - PullRequest
2 голосов
/ 07 марта 2020

Я пытаюсь использовать следующее для непрерывного чтения данных с последовательного порта:

var serialPort = new SerialPort("COM7", 38400, Parity.None, 8, StopBits.One);

var buffer = new byte[256];

Action read = null;

AsyncCallback callback = delegate (IAsyncResult ar)
{
    int len = serialPort.BaseStream.EndRead(ar);

    // do something with data

    read();
};

read = delegate
{
    serialPort.BaseStream.BeginRead(buffer, 0, buffer.Length, callback, null);
};

serialPort.Open();

while (true)
{
    read();

    //Thread.Sleep(100);
}

Это приводит к утечке памяти из-за постоянно растущего числа следующих объектов:

  • ManualResetEvent
  • Microsoft.Win32.SafeHandles.SafeWaitHandle
  • ThreadPoolBoundHandleOverlapped
  • OverlappedData
  • SerialStream + 101 * * 101 * Объект * 1015 сохраняются даже после прохода сборки мусора.

    Пример выше - минимальный воспроизводимый пример. Я убрал «сделать что-то с данными», потому что проблема возникает с обработкой данных или без нее на этом этапе.

    Thread.Sleep в то время как l oop только замедляет потребление памяти. Я оставил это так, чтобы он ясно показал проблему. Приведенный выше пример потребляет приблизительно 650 МБ за 20 секунд на моем компьютере.

    Проблема возникает в обоих случаях: NET Core 3.1 и. NET Framework 4.8.

    Я уверен, что я Я делаю что-то не так, я просто не вижу, что это на данный момент.

1 Ответ

1 голос
/ 07 марта 2020

Это приводит к утечке памяти из-за постоянно увеличивающегося ...

Просто потому, что вы бесконечно зацикливаете BeginRead операций до того, как существующая завершена с помощью:

while (true)
{
    read(); // <-- this delegate calls `BeginRead` thus starting ANOTHER verlapped read operation

    //Thread.Sleep(100);
}

Измените его на что-то вроде:

serialPort.Open();
read(); // kick off initial read

while (/* some condition*/)
{
}

// quit

Ваш обратный вызов делает правильную вещь, гарантируя, что другая операция чтения с перекрытием начинается только после завершения предыдущего:

AsyncCallback callback = delegate (IAsyncResult ar)
{
    int len = serialPort.BaseStream.EndRead(ar);

    // do something with data

    read(); // <--- correctly initiates another read since the existing is complete
};

...