当前位置: 首页 > 知识库问答 >
问题:

一个完成的await会在一个新线程上继续它自己的异步方法吗?[重复]

丰博
2023-03-14

我了解异步/等待的基本原理,但是我看到它提到了很多等待不会创建新线程。我可以尊重这一点,因为waits可以使用硬盘I / O或网卡或CPU以外的其他不需要新线程的东西。

我想澄清的是,在等待完成后,异步函数的其余部分是否在新线程上执行?从我自己的内部测试中可以看出,它是按照以下代码执行的:

public static class Program
        {
            private static void Main(string[] args)
            {
                Thread.CurrentThread.Name = "ThisThread";
                AnAsyncMethod();
                while (true)
                {
                    print("External While loop. Current Thread: " + Thread.CurrentThread.Name);
                    Thread.Sleep(200);
                }
            }

            public static async void AnAsyncMethod()
            {
                for (int i = 0; i < 10; i++)
                {
                    FakeWork();
                    print("doing fake pre-await work. Current Thread: " + Thread.CurrentThread.Name);
                }
                await Task.Run(FakeWork);
                for (int i = 0; i < 10; i++)
                {
                    FakeWork();
                    print("doing fake post-await work. Current Thread: " + Thread.CurrentThread.Name);
                }
                print("hello");
            }

            public static void FakeWork()
            {
                Thread.Sleep(100);
            }

        }

从这个例子来看,异步方法中似乎使用了相同的线程,直到它遇到第一个wait,此时控制权返回给调用者,程序继续。当wait完成时,一个新的单独线程将在异步方法中继续前进,而不是封送早期的线程并强制它继续自己。这个新线程与现在在其他地方使用的早期线程同时执行。

这是正确的吗?

共有2个答案

姜聪
2023-03-14

我已修改您的代码以显示线程ID。

class Program
    {
        static  void Main(string[] args)
        {
            System.Threading.Thread.CurrentThread.Name = "ThisThread";
            AnAsyncMethod();
            while (true)
            {
                Console.WriteLine("External While loop. Current Thread: " + $"{System.Threading.Thread.CurrentThread.Name}- {System.Threading.Thread.CurrentThread.ManagedThreadId}");
                System.Threading.Thread.Sleep(200);

            }
        }

        public static async void AnAsyncMethod()
        {
            for (int i = 0; i < 10; i++)
            {
                FakeWork();
                Console.WriteLine("doing fake pre-await work. Current Thread: " + $"{System.Threading.Thread.CurrentThread.Name}- {System.Threading.Thread.CurrentThread.ManagedThreadId}");
            }
            await System.Threading.Tasks.Task.Run(FakeWork);// Task.Run(FakeWork);
            for (int i = 0; i < 10; i++)
            {
                FakeWork();
                Console.WriteLine("doing fake post-await work. Current Thread: " + $"{System.Threading.Thread.CurrentThread.Name}- {System.Threading.Thread.CurrentThread.ManagedThreadId}");
            }
            Console.WriteLine("hello");
        }

        public static void FakeWork()
        {
            System.Threading.Thread.Sleep(100);
             StackTrace stackTrace = new StackTrace();

        Console.WriteLine($"Caller of Fakework method is  {stackTrace.GetFrame(1).GetMethod().Name}. Current Thread: " + $"{System.Threading.Thread.CurrentThread.Name}- {System.Threading.Thread.CurrentThread.ManagedThreadId}");

        }
    }

现在,您将在单独的线程中获取帖子等待。现在发生这种情况是因为

当它调用 Task.Run 时,它会将线程池上昂贵的 CPU 密集型操作 Fakework() 排队,并接收 Task 句柄。Fakework() 最终在下一个可用线程上并发运行

有关更多参考资料,请阅读“cpu绑定方法的异步”

https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth

因此,异步等待作业是在线程池上调度任务,并基于默认调度算法,它将使用相同的线程或派生出新的线程

太叔凌龙
2023-03-14

控制台应用程序上的行为与 WinForms 应用程序上的行为不同。

在控制台应用程序上,默认情况下,延续在默认的TaskScheduler上运行,该调度器在可用的工作线程池上运行任务的延续。在WinForms中,这种情况不会发生,因为在WinForm中,延续被发布回UI线程。

考虑以下示例:

    static async Task DoSomeThingAsync(string name)
    {
        Console.WriteLine($"{name} is starting on thread {Thread.CurrentThread.ManagedThreadId}");
        for (int i = 0; i < 5; i++)
        {
            await Task.Delay(1000);
            Console.WriteLine($"{name} is now on thread {Thread.CurrentThread.ManagedThreadId}");
        }

    }
    static async Task Main(string[] args)
    {
        var task1 = DoSomeThingAsync("Harry");
        var task2 = DoSomeThingAsync("Fred");
        var task3 = DoSomeThingAsync("Jack");

        Task[] MyTasks = new Task[3]{ task1, task2, task3 };

        await Task.WhenAll(MyTasks);

        Console.WriteLine("All tasks have completed.");
    }

控制台输出中的结果:

Harry is starting on thread 1
Fred is starting on thread 1
Jack is starting on thread 1
Jack is now on thread 4
Fred is now on thread 5
Harry is now on thread 6
Fred is now on thread 8
Harry is now on thread 5
Jack is now on thread 6
Fred is now on thread 7
Harry is now on thread 6
Jack is now on thread 5
Harry is now on thread 7
Fred is now on thread 5
Jack is now on thread 6
Harry is now on thread 5
Jack is now on thread 6
Fred is now on thread 9
All tasks have completed.

你会注意到代码正在执行的实际线程会跳来跳去,在我的系统上,我看到了六个不同的线程——你的里程会有所不同。

但重要的是,第一个<code>控制台。WriteLine()始终在主线程上运行,即线程一。

在控制台应用程序中,您不需要使用任务。Run()让CPU密集型任务在不同的线程池上运行。简单地等待一个任务,

await Task.Yield();
 类似资料:
  • 我试图构建一个实现Runnable的任务对象,该对象通过begin()方法生成一个新线程,将其自身用作线程中的Runnable对象? 然后... 这可能吗?或者抽象的可运行类在新线程中使用自己会有问题吗?

  • 我有两个JS函数。一个叫另一个。在调用函数中,我想调用另一个函数,等待该函数完成,然后继续。例如,伪代码: 我想出了这个解决方案,但不知道这是否是一个明智的方法。 这合法吗?有没有更优雅的处理方法?也许用jQuery?

  • 目前,我使用的是firebase实时数据库。因此,我的数据更改来自另一个线程。因此,我无法控制何时更新新的数据。然后我如何知道何时调用以刷新UI? 谢谢

  • 我如何启动两个线程,其中thread1首先执行,thread2在thread1结束时启动,而主方法线程可以在不锁定其他两个线程的情况下继续工作? 我尝试了join(),但是它需要从线程调用,线程必须等待另一个线程,没有办法执行类似thread2.join(thread1)的操作;因此,如果我在main()中调用join,我将有效地停止主线程的执行,而不仅仅是Thread2的执行。 #编辑:为什么我

  • 我试过这样的事情: create(new observable.onsubscribe>(){@override public void call(subscriber>subscriber){subscriber.onnext(getList());subscriber.oncompleted();}}).subscribe... 但是这个订阅在一个迭代上,onNext只传递完整的列表,而不是列

  • 问题内容: 我使用的是Spring 4,我注意到了一个奇怪的行为……如果我从普通实例方法多次调用异步方法,那么它们都将在不同的线程中调用,并在随机时间完成。但是,如果我多次从另一个异步方法中调用一个异步方法,那么它们将按顺序完成。我有这样的事情: 我正在使用默认的异步执行器。我应该换一个吗?但是,该执行程序不会重用任何线程,而是每次都启动另一个线程,因此应该没问题……这仅仅是巧合吗?但是我尝试了十