C# 具有任务控制的异步控制台

C# 具有任务控制的异步控制台,c#,.net,asynchronous,console,task-parallel-library,C#,.net,Asynchronous,Console,Task Parallel Library,请帮我找到正确的解决方案。 主要问题是等待程序通过控制台完成,同时监控任务 我写了一些原型,但我不确定这是否有效——在这种方法中,我们需要额外的线程等待控制台的操作。我看不到替代方案,因为Console不支持某种类型的Console.ReadLineAsync异步 更新: 我有两个工作任务task1和task2。它们模拟了一些真实的工作。 该程序是一个控制台。因此,我们需要给用户一个停止程序的机会。默认情况下,在控制台中,这是通过期望通过控制台任务按Enter键来完成的 问题是。如何等待工作线程

请帮我找到正确的解决方案。 主要问题是等待程序通过控制台完成,同时监控任务

我写了一些原型,但我不确定这是否有效——在这种方法中,我们需要额外的线程等待控制台的操作。我看不到替代方案,因为Console不支持某种类型的Console.ReadLineAsync异步

更新: 我有两个工作任务task1和task2。它们模拟了一些真实的工作。 该程序是一个控制台。因此,我们需要给用户一个停止程序的机会。默认情况下,在控制台中,这是通过期望通过控制台任务按Enter键来完成的

问题是。如何等待工作线程完成并监视用户发出的停止命令

static void Main(string[] args)
{
  CancellationTokenSource mycts = new CancellationTokenSource();
  var task1 = Task.Run(() =>
  {
  // doing some work, that can throw exception
  Thread.Sleep(1000);

  // how to avoid this closuring ?
  mycts.Token.ThrowIfCancellationRequested();

  throw new InvalidOperationException("test");
  }).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?

  var task2 = Task.Run(() =>    
  {
  // doing some work, that can throw exception
  Thread.Sleep(5000);

  // again closuring
  mycts.Token.ThrowIfCancellationRequested();
  throw new InvalidOperationException("test");
  }).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?


  // I do not know how to do better with Console !!
  var consoleTask = Task.Factory.StartNew((cts) =>
  {
  Console.WriteLine("Press Enter to exit");
  Console.ReadLine();
  }, mycts).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?

  // Waiting for the Completion or Exception
  Task.WaitAny(task1, task2, consoleTask);

  // Now waiting for the completion of workflow
  try
  {
    Task.WaitAll(task1, task2);
  }
  catch (Exception ex)
  {
    // log faulted tasks
  }

  //Exit
  }

由于console没有SynchronizationContext,所以在执行异步操作时,如果不阻塞主线程,您将无能为力

但是,如果您只是以异步方式编写代码,并以尽可能简单的方式阻塞代码,则会简单得多。我建议将所有代码移动到async MainAsync并阻塞一次:

static void Main()
{
    MainAsync().Wait();
}

static async Task MainAsync()
{
    // manage tasks asynchronously
}
您可以做的不是阻塞,而是使用自定义上下文来执行异步操作,如。这样可以避免在任务上同步阻塞:


您应该遵循以下几条准则:

不要使用ContinueWith。使用wait代替。 不要使用Task.Factory.StartNew。使用任务。改为运行。 不要混合使用阻塞代码和异步代码。在控制台应用程序的情况下,通常最好只让Main调用mainsync并等待返回的任务。对于大多数应用程序,这是唯一应该使用的阻塞。 我不知道怎样才能避免这种情况?意味着

对于ReadLine和其他控制台方法,您是正确的,不幸的是没有异步方法。使用单独的线程可能会起作用,但Console类更具体地说,Console输入和输出流有一些不寻常的锁,因此我不确定这是否会起作用:

static void Main(string[] args)
{
  MainAsync().Wait();
}
static CancellationTokenSource mycts = new CancellationTokenSource();
static async Task MainAsync()
{
  try
  {
    var task1 = CancelAfterSuccessfulCompletionAsync(
        Task.Run(() => SomeWorkThatCanThrowException()));
    var task2 = CancelAfterSuccessfulCompletionAsync(
        Task.Run(() => OtherWorkThatCanThrowException()));
    var consoleTask = CancelAfterSuccessfulCompletionAsync(
        Task.Run(() => MonitorConsole()));
    await Task.WhenAll(task1, task2, consoleTask);
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex);
  }
}
static void OtherWorkThatCanThrowException()
{
  Thread.Sleep(5000);
  mycts.Token.ThrowIfCancellationRequested();
  throw new InvalidOperationException("test");
}
static void SomeWorkThatCanThrowException()
{
  Thread.Sleep(1000);
  mycts.Token.ThrowIfCancellationRequested();
  throw new InvalidOperationException("test");
}
static void MonitorConsole()
{
  Console.WriteLine("Press Enter to exit");
  Console.ReadLine();
}
static async Task CancelAfterSuccessfulCompletionAsync(Task task)
{
  await task;
  mycts.Cancel();
}

这不正是我需要的。我需要以某种方式在控制台中为用户提供一个stop命令,即按Enter键退出。你打算怎么做?我们会在这段代码中结束MyCT吗?如何避免呢?取消标记源mycts;var task1=Task.Run=>{mycts.Token.ThrowIfCancellationRequested;}@你为什么要回避它?我找到了解决闭包问题的方法。在这里,阅读.NET 4.5中“将闭包信息编码到任务的状态对象”一章中的TPL性能改进非常有趣。@SoaperPlus:您不需要这样做。没有问题。你不应该有任务。WaitAlltask1,task2;因为如果WaitAny由于异常而返回,那么您将需要点击enter退出。它会让你疯狂地点击回车键,以防有一个异常被愚蠢地抛出?我希望在异常处理块之后,程序将正确完成。HmmIt将正确完成,但您明确要求转到所有任务的末尾。虽然我没有试过,但它看起来就是这样。我可能错了
static void Main(string[] args)
{
  MainAsync().Wait();
}
static CancellationTokenSource mycts = new CancellationTokenSource();
static async Task MainAsync()
{
  try
  {
    var task1 = CancelAfterSuccessfulCompletionAsync(
        Task.Run(() => SomeWorkThatCanThrowException()));
    var task2 = CancelAfterSuccessfulCompletionAsync(
        Task.Run(() => OtherWorkThatCanThrowException()));
    var consoleTask = CancelAfterSuccessfulCompletionAsync(
        Task.Run(() => MonitorConsole()));
    await Task.WhenAll(task1, task2, consoleTask);
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex);
  }
}
static void OtherWorkThatCanThrowException()
{
  Thread.Sleep(5000);
  mycts.Token.ThrowIfCancellationRequested();
  throw new InvalidOperationException("test");
}
static void SomeWorkThatCanThrowException()
{
  Thread.Sleep(1000);
  mycts.Token.ThrowIfCancellationRequested();
  throw new InvalidOperationException("test");
}
static void MonitorConsole()
{
  Console.WriteLine("Press Enter to exit");
  Console.ReadLine();
}
static async Task CancelAfterSuccessfulCompletionAsync(Task task)
{
  await task;
  mycts.Cancel();
}