Task.Factory.StartNew没有运行我的预期

时间:2017-02-09 20:24:52

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

我一直试图确定与我的其余代码同时/并行运行代码的最佳方式,可能是使用了一个线程。根据我的阅读,在现代C#中使用Thread类型是禁忌。最初我认为Parallel.Invoke(),但结果是阻止调用,直到所有内部工作完成。

在我的应用程序中,我不需要等待任何事情来完成,我不关心获得结果,我需要完全独立于当前线程的代码。基本上是"火灾和忘记"想法。

根据我的理解,Task.Factory.StartNew()是与当前运行的代码同时/并行运行一段代码的正确方法。

基于此,我认为下面的代码会随机打印出来" ABABBABABAA"。

void Main()
{
    Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Console.Write("A");
        }
    });

    for (int i = 0; i < 10; i++)
    {
        Console.Write("B");
    }
}

然而,它:

  1. 打印出来&#34; BBBBBBBBBBAAAAAAAAAA&#34;
  2. 如果我将Task.Factory.StartNew与for交换,反之亦然,打印出相同的序列,这看起来很奇怪。
  3. 因此,这让我认为Task.Factory.StartNew()实际上从未将工作安排到另一个线程,几乎就像调用StartNew是阻塞调用一样。

    鉴于要求我不需要获得任何结果或等待/ await,我是否更容易创建新的Thread并在那里运行我的代码?我遇到的唯一问题是使用Thread似乎与现代最佳实践和#34; C#6.0中的并发和#34;规定:

      

    一旦你键入新的Thread(),它就结束了;您的项目已有遗留代码

2 个答案:

答案 0 :(得分:4)

实际上,Task.Factory.StartNew确实在一个单独的线程上运行,每次失去竞争的唯一原因是因为任务创建时间。这是证明它的代码

static void Main(string[] args)
    {
        Task.Factory.StartNew(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                Console.Write("A");
                Thread.Sleep(1);
            }
        });

        for (int i = 0; i < 10; i++)
        {
            Console.Write("B");
            Thread.Sleep(1);
        }
        Console.ReadKey();
    }

答案 1 :(得分:1)

  

在我的应用程序中,我不需要等待任何事情来完成,我不关心获得结果,我需要完全独立于当前线程的代码。基本上是&#34;火灾和忘记&#34;想法。

你确定吗? “即发即忘”的确意味着&#34;我完全可以忽略异常。&#34;由于这不是大多数人想要的,因此一种常见的方法是对工作进行排队并保存表示该工作的Task,并在&#34; end&#34;无论你的程序做了什么,只是为了确保不完全不能完成的工作确实成功完成。

  

我一直试图确定与我的其余代码同时/并行运行代码的最佳方式,可能是使用了一个线程。从我所读过的内容来看,在现代C#中使用Thread类型是禁忌。最初我认为是Parallel.Invoke(),但结果是阻塞调用,直到所有内部工作完成。

是的,并行代码将阻塞调用线程。这可以通过将并行调用包装在Task.Run中来避免(在我的书中的食谱7.4中说明)。

在您的特定情况下(例如,在即发即弃的情况下),您可以放弃await并且只有Task.Run。虽然如上所述,将Task隐藏在某个地方并await以后可能会更好。

  

根据我的理解,Task.Factory.StartNew()是与当前运行的代码同时/并行运行一段代码的正确方法。

不,StartNew is dangerous并且只应作为最后的手段使用。正确的技术是Task.Run,如果你有真正的并行工作(即很多块的CPU绑定代码),那么Task.Run包装Parallel / PLINQ最好。

  

基于此,我认为下面的代码会随机打印出来&#34; ABABBABABAA&#34;。

作为others have noted,这只是竞争条件。将工作排队到线程池需要花费时间,计算机可以非常快地计算到10。 (写入输出要慢很多,但这里仍然太快)。 Task.Run(或手动Thread)会出现同样的问题。