【问题标题】:Asyncronous console with task controlling具有任务控制的异步控制台
【发布时间】:2015-10-06 14:03:24
【问题描述】:

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

我写了一些原型,但我不确定它是否有效——在这种方法中,我们花费了一个额外的线程来等待来自控制台的操作。我没有看到替代品,因为 Console 不支持异步(某种 Console.ReadLineAsync)。

更新: 我有两个工作任务(task1、task2)。它们模拟了一些实际工作。 该程序是一个控制台。所以我们需要给用户一个停止程序的机会。默认情况下,在控制台中,这是通过按“Enter”完成的(通过consoleTask)。

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

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
  }

【问题讨论】:

  • 你不应该有Task.WaitAll(task1, task2);,因为如果WaitAny因为异常而返回,那么你将需要按回车键退出。 (它会让你像疯了一样敲回车,以防万一有异常被默默抛出)
  • 真的吗?我预计在异常处理块之后,程序将正确完成。嗯
  • 它将正确完成,但您明确要求结束所有任务。虽然我没有尝试过,但这就是它的样子。我可能错了

标签: c# .net asynchronous console task-parallel-library


【解决方案1】:

您应该遵循一些准则:

  1. 不要使用ContinueWith。请改用await
  2. 不要使用Task.Factory.StartNew。请改用Task.Run
  3. 不要混合阻塞和异步代码。对于控制台应用程序,通常最好让Main 调用MainAsync 并等待返回的任务。对于大多数应用程序,这是您应该使用的唯一阻塞。

我不确定how to avoid this closuring? 是什么意思。

ReadLine(和其他控制台方法)的情况下,你是对的,不幸的是没有异步方法。 可能可以使用单独的线程,但 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();
}

【讨论】:

  • 我们会在这段代码中的“mycts”上获得“关闭”吗?以及如何避免? CancellationTokenSource mycts; var task1 = Task.Run(() => { mycts.Token.ThrowIfCancellationRequested(); });
  • @SoaperPlus:你为什么要避免它?
  • 我找到了解决闭包问题的方法。在“将闭包信息编码到任务的状态对象”一章中阅读“.NET 4.5 中的 TPL 性能改进”blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235962.aspx 非常有趣。
  • @SoaperPlus:你不需要这样做。那里没有“问题”。
【解决方案2】:

由于控制台没有 SynchronizationContext,因此在执行异步操作时,如果不阻塞主线程,您将无能为力。

但是,如果您只是编写代码,就好像它是异步的并以最简单的方式进行阻塞,则要简单得多。我建议将您的所有代码移至异步 MainAsync 并阻塞一次:

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

static async Task MainAsync()
{
    // manage tasks asynchronously
}

您可以做的不是阻塞,而是使用自定义上下文来执行异步操作,例如Stephen Cleary's AsyncContext。这使您可以避免在 Task 上同步阻塞:

static void Main()
{
    AsyncContext.Run(MainAsync);
}

【讨论】:

  • 这不是我所需要的。我需要以某种方式在控制台中向用户提供停止命令(即“按 Enter 退出”)。你打算怎么做?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-04
  • 2013-12-11
  • 1970-01-01
  • 1970-01-01
  • 2019-09-14
  • 1970-01-01
相关资源
最近更新 更多