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

异步-保持在当前线程上?

贾成天
2023-03-14

我读过Eric lippert关于<code>异步关键字的困惑。他说:

它(async)意味着“此方法包含包含等待异步操作的控制流,因此将由编译器重写为连续传递样式,以确保异步操作可以在正确的位置恢复此方法。”异步方法的关键在于尽可能多地停留在当前线程上

这个我不懂。如果我执行一个异步方法(< code>Task)并且它运行了,那么它肯定会在另一个线程上运行。

此外,如果我编写一个方法使用wait,(imho)它会释放正常的控制流,并且代码在另一个线程上被重构为“ContinueAnd”。

我用(控制台)测试了它:

/*1*/   public void StartChain()
/*2*/   {
/*3*/           var a = FuncA();
/*4*/           Console.WriteLine(a.Result);
/*5*/   }
/*6*/   
/*7*/   public async Task < int > FuncA()
/*8*/   {
/*9*/           Console.WriteLine("A--" + Thread.CurrentThread.ManagedThreadId);
/*10*/           var t = await FuncB();
/*11*/           Console.WriteLine("B--" + Thread.CurrentThread.ManagedThreadId);
/*12*/           return t;
/*13*/   }
/*14*/   
/*15*/   public async Task < int > FuncB()
/*16*/   {
/*17*/           Console.WriteLine("C--" + Thread.CurrentThread.ManagedThreadId);
/*18*/           await Task.Delay(2000);
/*19*/           Console.WriteLine("D--" + Thread.CurrentThread.ManagedThreadId);
/*20*/           return 999;
/*21*/   }
/*22*/   
/*23*/   void Main()
/*24*/   {
/*25*/           StartChain();
/*26*/   }
/*27*/   

结果是:

A--7
C--7
D--17         <-----D  and B are on different thread
B--17
999

那么,Eric说“留在当前线程上”是什么意思呢?

在< code>asp.net中,它还返回不同的线程ID。

public async Task<int> FuncA()
{
    Response.Write("<br/>C----" + Thread.CurrentThread.ManagedThreadId);
    var t = await FuncB();
    Response.Write("<br/>D----" + Thread.CurrentThread.ManagedThreadId);
    return t;
}

public async Task<int> FuncB()
{
    Response.Write("<br/>E----" + Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(2000);
    Response.Write("<br/>F----" + Thread.CurrentThread.ManagedThreadId);
    return 999;
}



protected async void Page_Load(object sender, EventArgs e)
{
    Response.Write("<br/>A----" + Thread.CurrentThread.ManagedThreadId);
    var a=await FuncA();
    Response.Write("<br/>B----" + Thread.CurrentThread.ManagedThreadId);

}

A----8
C----8
E----8
F----9
D----9
B----9

(得到答案后)

似乎线程仅在GUI应用程序中提供:。我在winform中运行此代码

  public async Task<int> FuncA()
        {
            textBox1.Text +=Environment.NewLine+ "\nC----" + Thread.CurrentThread.ManagedThreadId;
            var t = await FuncB();
            textBox1.Text += Environment.NewLine + "\nD----" + Thread.CurrentThread.ManagedThreadId;
            return t;
        }

        public async Task<int> FuncB()
        {
            textBox1.Text += Environment.NewLine + "\nE----" + Thread.CurrentThread.ManagedThreadId;
            await Task.Delay(2000);
            textBox1.Text += Environment.NewLine + "\nF----" + Thread.CurrentThread.ManagedThreadId;
            return 999;
        }




        private async void Form1_Load(object sender, EventArgs e)
        {
            textBox1.Text += Environment.NewLine + "\nA----" + Thread.CurrentThread.ManagedThreadId;
            var a = await FuncA();
            textBox1.Text += Environment.NewLine + "\nB----" + Thread.CurrentThread.ManagedThreadId;
        }

共有3个答案

饶骁
2023-03-14

Eric Lippert的“线程”术语被简化了。我的博客上有一个<code>异步/<code>等待

如果您在UI上下文中,则该上下文是单个UI线程,并且< code>async方法将在该线程上继续。否则,规则就有点复杂了。特别是,控制台应用程序不提供任何上下文,所以默认情况下< code>async方法在线程池上恢复。

郗福
2023-03-14

添加了异步/等待支持,以帮助程序员编写不会冻结的 GUI。WinRT 在应用商店应用中特别有用,也是将其添加到 C# v5 的核心原因,它是一个非常不友好的 API,具有许多异步方法。

“保持在同一线程上”场景在GUI应用程序中非常重要,这是必需的,因为GUI不是线程安全的。然而,它确实需要调度程序循环(又名Application.Run),这是让异步代码在同一线程上恢复的唯一方法。该循环是生产者-消费者问题的核心解决方案。

很明显,您的程序没有,看起来很像控制台模式的应用程序。因此,您不会得到这种行为,它会在工作线程上恢复。

没什么大问题,你实际上不需要它在同一个线程上恢复,因为无论如何控制台都是线程安全的。嗯,主要是,不包括在. NET 4.5中添加的锁。当您要求输入时。这当然也意味着您也没有async/wait的用途,任务也可以正常工作。

龙哲
2023-03-14

如果我执行一个异步方法并运行它,它肯定会在另一个线程上运行。

不,它通常在另一个线程上运行。它不一定在另一个线程上运行。

暂时停止考虑线程,考虑异步的本质。异步的本质是:

  • 我有一些当前正在执行的工作流。
  • 在获得信息 X 之前,我无法继续执行此工作流。
  • 我将做其他事情,直到我获得信息X。
  • 在未来的某个时候,一旦我有了X,我就会回到我在工作流程中离开的地方并继续。

假设你在做你的税务工作,在这个复杂的工作流程中,你需要执行大量的额外任务。你可以做一些手术,然后记住你在哪里,然后去吃午饭。然后回来再做几次手术,然后记住你在哪里,然后喂猫。然后回来再做几次手术,然后记得你在哪里,洗盘子。然后完成计算,然后继续您在工作流程中停止的位置。

这是一个异步计算,但只需要一个工作人员来完成。拥有多个工作人员只是执行异步的一种特别方便的方法,不是必须的。

 类似资料:
  • 我知道使用无线程异步有更多线程可用于服务输入(例如HTTP请求),但我不明白当异步操作完成并且需要一个线程来运行它们的延续时,这如何不可能导致线程饥饿。 假设我们只有3个线程 并且它们在需要线程的长时间运行的操作中被阻塞(例如在单独的db服务器上进行数据库查询) 使用async Wait,您可以 然而,在我看来,这可能会导致“正在进行”的异步操作过剩,如果太多的操作同时完成,那么就没有线程可以运行

  • 线程中使用 java.lang.Runnable 如果用户在代码中通过 java.lang.Runnable 新启动了线程或者采用了线程池去异步地处理一些业务,那么需要将 SOFATracer 日志上下文从父线程传递到子线程中去,SOFATracer 提供的 com.alipay.common.tracer.core.async.SofaTracerRunnable 默认完成了此操作,大家可以按照

  • 正在使用和原始人的线程吗? 许多月前,我学会了如何在Android上编写多线程Java代码。我记得我必须创建线程、启动线程等等。 现在我正在学习Javascript,我刚刚学习了和。 例如: 这看起来比我以前做的简单多了,而且更直观。 将首先启动,然后启动快速函数(),然后将等待,直到两个函数在日志记录之前解决-和和可能同时运行。我希望这最终取决于浏览器是否是独立的线程。但看起来它走路和说话就像粗

  • 因为我们知道progressdialog需要一个参数上下文或获取片段的活动,但在解除对话框后,它转到了另一个片段,我将其设置为mainactivity默认片段为homepage 这是我课堂上使用的方法

  • Dart 的库充满了返回 Future 或 Stream 对象的函数。这些函数是“异步的”:它们在设置一个可能比较耗时的操作(比如 I/O)后返回,而不去等待操作完成。 关键字 async 和 await 支持异步编程,可以使你用看起来像同步的方式编写异步代码。 处理 Futures 当你需要一个已完成的 Future 的结果时,你有两个选择: 使用 async 和 await。 使用 Futur

  • 我正在为一个大型应用程序编写自动化测试。这些测试中的一些很容易成为<code>异步,它只提供<code>async 测试应用程序的一些关键方面如下: 这是一个巨大的ASP.NET应用程序(尽管代码在通过单元测试执行时没有在ASP.NET上下文中运行)。 在其核心中,它严重依赖于每个线程缓存上下文信息(例如活动用户的整个权限方案)的静态对象。 现在,我的问题是当在方法中使用时,延续可能发生在与以前不