Skip to content

Instantly share code, notes, and snippets.

@lucacicada
Created April 25, 2022 14:12
Show Gist options
  • Save lucacicada/2e15ac64c52c4f244acb741bb839243b to your computer and use it in GitHub Desktop.
Save lucacicada/2e15ac64c52c4f244acb741bb839243b to your computer and use it in GitHub Desktop.
C# Console concurrent thread-safe write on the same line

C# Concurrent Console.Write

This method can be used to write to the console in a multithreaded environment, it prints on the current line, clearing any text was previously written.

It also normalizes the text, removing duplicates spaces and newlines, trims the text so it does not overflow to the next line.

// write on the current line, clearing the old text
ConsoleEx.ReWrite("some text");

// clear the current line
ConsoleEx.ReWrite(null);

Notes

Writing on the console can be problematic and can be quite slow.

It is suggested that you do not print too often, especially in performance critical code.

namespace System;
using System.Text;
using System.Threading;
public static class ConsoleEx
{
/// <summary>
/// Keep track of concurrent writing.
/// </summary>
private static int writing;
/// <summary>
/// Writes the specified string value to the standard output stream over the current line.
/// </summary>
/// <param name="value">The value to write.</param>
/// <exception cref="IOException">An I/O error occurred.</exception>
public static void ReWrite(String value)
{
if (Interlocked.Exchange(ref writing, 1) == 0)
{
try
{
int cursorLeft = Console.CursorLeft;
int bufferWidth = Console.BufferWidth;
int printWidth = bufferWidth - 1; // for the null terminator
// create the string builder
// value.Length + bufferWidth can be dangerous, value.Length could be huge
var sb = new StringBuilder(240);
// backspace a number of times equal to the current buffer width
sb.Append('\b', bufferWidth);
// do not print consecutive whitespaces
bool skipWhiteSpace = false;
// sanity check, ignore empty strings
if (value != null && value.Length > 0)
{
// the string builder length cannot exceed (printWidth + bufferWidth)
for (int i = 0; i < value.Length && sb.Length < printWidth + bufferWidth; i++)
{
char item = value[i];
if (char.IsWhiteSpace(item))
{
if (!skipWhiteSpace)
{
// add a whitespace
skipWhiteSpace = true;
sb.Append(' ');
}
}
else
{
// add a non whitespace
skipWhiteSpace = false;
sb.Append(item);
}
}
}
// calculate the pad relative to the cursor position
int pad = cursorLeft - (sb.Length - bufferWidth);
if (pad > printWidth) pad = printWidth;
if (pad > 0)
{
// insert white spaces to clear the current text
sb.Append(' ', pad);
// backspace to preserve the cursor position
sb.Append('\b', pad);
}
// print the string
Console.Write(sb.ToString());
}
finally
{
_ = Interlocked.Exchange(ref writing, 0);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment