【问题标题】:How to run C# task method on separate thread without using Task.Run()如何在不使用 Task.Run() 的情况下在单独的线程上运行 C# 任务方法
【发布时间】:2017-04-19 06:40:20
【问题描述】:

我有控制台应用程序,其中有 2 个步骤。 Step1Step2。我希望 Step 1 在主线程上运行,Step 2 在后台的不同线程上运行,并且我使用 C# task object 来实现相同的目的。

见下面代码sn-p:

public static Task PrintCount()
{
     Console.WriteLine("Step 2");

     for (var i = 1; i <= 1000; i++)
     {
          Thread.Sleep(1000);
          Console.WriteLine(i);
     }

     return Task.FromResult(0); 
}

static void Main(string[] args)
{
     Task.Run(() => PrintCount());

     Console.WriteLine("Step 1");

     Console.Read();
}

工作得很好如果我使用 Task.Run().

运行 PrintCount() 方法

当我像下面提到的那样不使用它时,它会作为同步操作运行。

var taskResult = PrintCount();

在这种情况下,Step 2会先执行,完成1000次迭代,直到方法完成,然后才会执行Step 1

问题是——我如何在不使用 Task.Run() 的情况下运行第 2 步方法,但我仍然想在不同线程上以异步方式运行第 2 步方法。

我相信 PrintCount() 返回 Task 对象,如果我们不等待它,它将异步运行,但它的行为不是那样。

您能否建议我如何在不使用 Task.Run() 的情况下使用 Task 方法在不同的线程上运行 DoSomehing()

提前致谢!

更新:

 public static async Task PrintCount()
        {
            await Task.Delay(TimeSpan.FromSeconds(3));

            Console.WriteLine("Step 2");

            for (var i = 1; i <= 1000; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine(i);
            }

        }

这将在不同的线程上运行。

我的称呼没有改变。

var taskResult = PrintCount();

【问题讨论】:

  • 那么发生了什么?你已经说过什么是有效的,但你还没有说你当前的代码会发生什么。请注意,您实际上并不需要调用 Func&lt;Task&gt; 的重载 - 您可以将 PrintCount 方法更改为 void 方法,然后调用 Task.Run((Action) PrintCount);
  • 这与asp.net-mvc-4标签有什么关系也不清楚......
  • 但是为什么你不想使用Task.Run?这通常是您开始任务的方式。 Task.Run 做了什么你不喜欢的事情?你说它“不像那样表现”(异步),但它确实似乎适合我。是什么让你认为不是?同样,您并没有准确地说出您所观察到的出乎意料的内容。
  • @DamianT:PrintCount()返回的任务在这里基本无关。 delegate 在不同的线程中执行,同步调用PrintCount()。但是不需要额外的间接级别,当您可以让PrintCount 返回void 并像我之前展示的那样异步执行它时。你仍然没有解释你为什么不想使用Task.Run
  • 此时,我建议删除该问题,因为它基于一个错误的前提。

标签: c# multithreading c#-4.0 task-parallel-library


【解决方案1】:

如何在不使用 Task.Run() 的情况下在单独的线程上运行 C# 任务方法

好吧,为什么我不想使用 Task.Run() 因为我相信如果一个方法返回任务对象并且如果我们不等待,它将在不同的线程上运行,因为我们不等待它结果。有点即发即弃。

如果你不awaitTask.Run 返回的任务,那么是的,它是即发即弃。解决办法是awaitTask.Run返回的任务:

public static void PrintCount()
{
  Console.WriteLine("Step 2");

  for (var i = 1; i <= 1000; i++)
  {
    Thread.Sleep(1000);
    Console.WriteLine(i);
  }
}

static async Task Steps()
{
  var task = Task.Run(() => PrintCount());

  Console.WriteLine("Step 1");

  await task;
}

您的Main 可能看起来像这样,但我通常建议开发人员使用控制台应用程序以外的其他东西来“尝试”异步。因为Main 应该阻塞一个任务,但一般来说应该避免阻塞异步任务:

static void Main()
{
  Steps().GetAwaiter().GetResult();
  Console.Read();
}

【讨论】:

    【解决方案2】:

    PrintCount 的第一个实现确实是同步的,因为这就是您编写它的方式。当您调用该方法时,它只是开始执行每一行代码,点击Thread.Sleep 调用并阻塞线程,直到它们各自完成,直到最后,在最后,返回一个已经完成的Task

    避免使用Task.Run 的方法是对任何长时间运行的操作使用固有的异步操作。在您的情况下,您实际上没有任何长时间运行的 CPU 绑定工作;您唯一长时间运行的操作是等待一秒钟。使用Task.Delay 在固定时间内什么都不做。因此,如果您将 Thread.Sleep(...) 的用法替换为 await Task.Delay(...),则操作将固有异步,并且不需要使用 Task.Run 来执行此操作。

    在您的第二个实现中,您确实演示了Task.Delay 的适当用法,但是在等待异步操作之后,您只需使用Thread.Sleep 调用阻塞线程一段时间。就像以前一样,这是在不执行任何操作时阻塞线程,但不同之处在于代码在线程池线程中运行,因为它位于await 之后。 await 意味着该方法的其余部分将在等待的Task 完成后的某个稍后时间运行,完成的方式是让当前线程继续运行,然后让线程池线程运行当它能够运行时,该方法的其余部分。请注意,如果设置了 SynchronizationContext,那么它将用于运行延续,而不是线程池线程。

    【讨论】:

      【解决方案3】:

      创建的Task 对象只是对要完成的任务的描述。 Task 本身在您安排它之前不会执行,无论是使用静态 Task.Run() 方法还是使用 task.Start() 实例方法。

      【讨论】:

      • 我也可以通过等待来执行。例如等待任务方法()
      • 你也应该是awaiting Task.Run()
      • 这实际上取决于目标是什么。如果步骤 2 与步骤 1 并行进行,则无需等待。如果步骤 2 应该在步骤 1 之前完成而不阻塞步骤 1 线程 - 等待是必要的。
      • @DamianT 等待任务不会执行它。它只是告诉当前方法在Task 完成后继续工作。它对启动任务没有任何作用。
      • 您的第一句话是正确的,Task 确实表示要完成的工作。其余的都是错误的。你不使用Task.Run 来执行TaskTask.Run 是一种创建已经运行的任务的方法,它表示在线程池线程中执行一个方法(如果使用默认任务调度程序;如果使用非默认任务调度程序,它可能会以另一种方式执行)调度器)。 Task.Start 是您永远不应该使用的方法;根本没有充分的理由拥有未开始的Task。在编写良好的程序中,您通常不会使用 either 方法来开始 Task.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-05
      • 1970-01-01
      • 2012-11-08
      • 1970-01-01
      • 1970-01-01
      • 2019-03-04
      相关资源
      最近更新 更多