异步使用 Console.SetCursorPosition
Using Console.SetCursorPosition asynchronously
为了试验更新控制台中进度项的百分比,我制作了一个小型测试控制台应用程序:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleProgressTest
{
class Program
{
public class ConsoleItem
{
public string Item { get; set; }
public int ConsoleLocLeft { get; set; }
public int ConsoleLocTop { get; set; }
}
static void Main(string[] args)
{
List<string> tempList = GenerateTempList();
List<string> selection = GetSelectionFromList(tempList);
List<ConsoleItem> consoleItems = new List<ConsoleItem>();
foreach (string item in selection)
{
Console.Write($"Selected: \"{item}\" ");
ConsoleItem consoleItem = new ConsoleItem()
{
Item = item,
ConsoleLocLeft = Console.CursorLeft,
ConsoleLocTop = Console.CursorTop
};
consoleItems.Add(consoleItem);
Console.Write("\n");
}
int finalCursorLeft = Console.CursorLeft;
int finalCursorTop = Console.CursorTop;
Console.CursorVisible = false;
List<Task> progressTasks = new List<Task>();
foreach (ConsoleItem item in consoleItems)
{
Task itemTask = IncrementProgress(item);
progressTasks.Add(itemTask);
}
Task.WaitAll(progressTasks.ToArray());
Console.CursorVisible = true;
Console.SetCursorPosition(finalCursorLeft, finalCursorTop);
Console.WriteLine("All progress finished. Press any key to exit");
Console.Read();
}
private static List<string> GenerateTempList()
{
List<string> result = new List<string>()
{
"Item 0",
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5"
};
return result;
}
public static List<string> GetSelectionFromList(List<string> listToDisplay)
{
foreach (string item in listToDisplay)
Console.WriteLine("(" + listToDisplay.IndexOf(item) + ") " + item);
Console.WriteLine("Which # would you like?");
string input = Console.ReadLine();
List<string> result = new List<string>();
foreach (string indexStr in input.Split(','))
{
if (indexStr.Contains("-"))
{
int startOfRange = Convert.ToInt32(indexStr.Split('-')[0]);
int endOfRange = Convert.ToInt32(indexStr.Split('-')[1]);
for (int i = startOfRange; i <= endOfRange; i++)
{
if (i < 0 || i > listToDisplay.Count - 1)
{
Console.WriteLine("Could not find index " + i);
continue;
}
result.Add(listToDisplay[i]);
}
}
else
{
int index = Convert.ToInt32(indexStr);
if (index < 0 || index > listToDisplay.Count - 1)
{
Console.WriteLine("Could not find index " + index);
continue;
}
result.Add(listToDisplay[index]);
}
}
return result;
}
public static async Task IncrementProgress(ConsoleItem item)
{
Random r1 = new Random((int)DateTime.Now.Ticks);
int millisecondDelay = r1.Next(1000, 5000);
Random r2 = new Random((int)DateTime.Now.Ticks);
int percentage = 0;
while (percentage < 100)
{
await Task.Run(() => Thread.Sleep(millisecondDelay));
percentage = r2.Next(percentage, 101);
UpdatePercentange(item, percentage);
}
}
private static void UpdatePercentange(ConsoleItem item, double percentage)
{
Console.SetCursorPosition(item.ConsoleLocLeft, item.ConsoleLocTop);
Console.Write($"({percentage}%)");
}
}
}
我在更新控制台时注意到一些奇怪的事情:
(注意当项目 2 达到 96% 时,该过程有时会如何重复)
但是如果我像这样在 UpdatePercentage
方法的开头输入 Debug.WriteLine
:
private static void UpdatePercentange(ConsoleItem item, double percentage)
{
Debug.WriteLine($"Updating percentage of {item.Item} at {item.ConsoleLocLeft}, {item.ConsoleLocTop} ({percentage})");
Console.SetCursorPosition(item.ConsoleLocLeft, item.ConsoleLocTop);
Console.Write($"({percentage}%)");
}
然后就可以正常工作了:
知道是什么原因造成的吗?
您可以使用通用对象锁定您的更新请求(一个对象 sync = new object() 就足够了),这样可以避免您的问题。
目前,情况是这样的:
- (线程 1)SetCursorPosition
- (线程 2) SetCursorPosition(光标现在位于线程 2 想要写入的位置)
- (线程 1 或 2)Console.Write
- (其他线程)Console.Write
因此您的控制台输出可能如下所示:
private static object _sync = new object();
private static void UpdatePercentange(ConsoleItem item, double percentage)
{
lock(_sync)
{
Console.SetCursorPosition(item.ConsoleLocLeft, item.ConsoleLocTop);
Console.Write($"({percentage}%)");
}
}
为了试验更新控制台中进度项的百分比,我制作了一个小型测试控制台应用程序:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleProgressTest
{
class Program
{
public class ConsoleItem
{
public string Item { get; set; }
public int ConsoleLocLeft { get; set; }
public int ConsoleLocTop { get; set; }
}
static void Main(string[] args)
{
List<string> tempList = GenerateTempList();
List<string> selection = GetSelectionFromList(tempList);
List<ConsoleItem> consoleItems = new List<ConsoleItem>();
foreach (string item in selection)
{
Console.Write($"Selected: \"{item}\" ");
ConsoleItem consoleItem = new ConsoleItem()
{
Item = item,
ConsoleLocLeft = Console.CursorLeft,
ConsoleLocTop = Console.CursorTop
};
consoleItems.Add(consoleItem);
Console.Write("\n");
}
int finalCursorLeft = Console.CursorLeft;
int finalCursorTop = Console.CursorTop;
Console.CursorVisible = false;
List<Task> progressTasks = new List<Task>();
foreach (ConsoleItem item in consoleItems)
{
Task itemTask = IncrementProgress(item);
progressTasks.Add(itemTask);
}
Task.WaitAll(progressTasks.ToArray());
Console.CursorVisible = true;
Console.SetCursorPosition(finalCursorLeft, finalCursorTop);
Console.WriteLine("All progress finished. Press any key to exit");
Console.Read();
}
private static List<string> GenerateTempList()
{
List<string> result = new List<string>()
{
"Item 0",
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5"
};
return result;
}
public static List<string> GetSelectionFromList(List<string> listToDisplay)
{
foreach (string item in listToDisplay)
Console.WriteLine("(" + listToDisplay.IndexOf(item) + ") " + item);
Console.WriteLine("Which # would you like?");
string input = Console.ReadLine();
List<string> result = new List<string>();
foreach (string indexStr in input.Split(','))
{
if (indexStr.Contains("-"))
{
int startOfRange = Convert.ToInt32(indexStr.Split('-')[0]);
int endOfRange = Convert.ToInt32(indexStr.Split('-')[1]);
for (int i = startOfRange; i <= endOfRange; i++)
{
if (i < 0 || i > listToDisplay.Count - 1)
{
Console.WriteLine("Could not find index " + i);
continue;
}
result.Add(listToDisplay[i]);
}
}
else
{
int index = Convert.ToInt32(indexStr);
if (index < 0 || index > listToDisplay.Count - 1)
{
Console.WriteLine("Could not find index " + index);
continue;
}
result.Add(listToDisplay[index]);
}
}
return result;
}
public static async Task IncrementProgress(ConsoleItem item)
{
Random r1 = new Random((int)DateTime.Now.Ticks);
int millisecondDelay = r1.Next(1000, 5000);
Random r2 = new Random((int)DateTime.Now.Ticks);
int percentage = 0;
while (percentage < 100)
{
await Task.Run(() => Thread.Sleep(millisecondDelay));
percentage = r2.Next(percentage, 101);
UpdatePercentange(item, percentage);
}
}
private static void UpdatePercentange(ConsoleItem item, double percentage)
{
Console.SetCursorPosition(item.ConsoleLocLeft, item.ConsoleLocTop);
Console.Write($"({percentage}%)");
}
}
}
我在更新控制台时注意到一些奇怪的事情:
(注意当项目 2 达到 96% 时,该过程有时会如何重复)
但是如果我像这样在 UpdatePercentage
方法的开头输入 Debug.WriteLine
:
private static void UpdatePercentange(ConsoleItem item, double percentage)
{
Debug.WriteLine($"Updating percentage of {item.Item} at {item.ConsoleLocLeft}, {item.ConsoleLocTop} ({percentage})");
Console.SetCursorPosition(item.ConsoleLocLeft, item.ConsoleLocTop);
Console.Write($"({percentage}%)");
}
然后就可以正常工作了:
知道是什么原因造成的吗?
您可以使用通用对象锁定您的更新请求(一个对象 sync = new object() 就足够了),这样可以避免您的问题。
目前,情况是这样的:
- (线程 1)SetCursorPosition
- (线程 2) SetCursorPosition(光标现在位于线程 2 想要写入的位置)
- (线程 1 或 2)Console.Write
- (其他线程)Console.Write
因此您的控制台输出可能如下所示:
private static object _sync = new object();
private static void UpdatePercentange(ConsoleItem item, double percentage)
{
lock(_sync)
{
Console.SetCursorPosition(item.ConsoleLocLeft, item.ConsoleLocTop);
Console.Write($"({percentage}%)");
}
}