C# 当任务在调用线程上时,它是如何工作的?

C# 当任务在调用线程上时,它是如何工作的?,c#,multithreading,asynchronous,C#,Multithreading,Asynchronous,正如我们所知,运行一个新的任务并不能保证生成一个新线程。我想知道两块代码(任务调用者的剩余代码和任务)如何在同一线程上同时阻止调用者阻塞 示例: 假设有一个windows窗体应用程序,其窗体名为Form1。处理两个事件: 按键,点击 this.KeyPreview = true; ... private void Form1_Click(object sender, EventArgs e) { Messag

正如我们所知,运行一个新的
任务
并不能保证生成一个新线程。我想知道两块代码(任务调用者的剩余代码和任务)如何在同一
线程上同时阻止调用者阻塞

示例

假设有一个windows窗体应用程序,其窗体名为
Form1
。处理两个事件:
按键
点击

        this.KeyPreview = true;
        ...
        private void Form1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Clicked");
        }

        private async void Form1_KeyPress(object sender, KeyPressEventArgs e)
        {
           Console.WriteLine("Caller threadId = " + Thread.CurrentThread.ManagedThreadId);
           await Task.Run(() =>
           {
              var start = DateTime.Now;
              Console.WriteLine("working asynchronously on thread: " + Thread.CurrentThread.ManagedThreadId);
              while ((DateTime.Now - start).TotalSeconds < 5)
                  ;                    
           });          
       }
this.KeyPreview=true;
...
私有void Form1\u单击(对象发送方,事件参数e)
{
MessageBox.Show(“单击”);
}
私有异步void Form1\u KeyPress(对象发送方,KeyPressEventArgs e)
{
Console.WriteLine(“调用程序threadId=“+Thread.CurrentThread.ManagedThreadId”);
等待任务。运行(()=>
{
var start=DateTime.Now;
WriteLine(“在线程上异步工作:+thread.CurrentThread.ManagedThreadId”);
while((DateTime.Now-start).TotalSeconds<5)
;                    
});          
}
如果我按下一个键,持续5秒钟,控制台中就会有东西被写入。同时,表单可以处理
单击事件并显示消息框。

您可能会发现我的方法很有用

正如其他人所指出的,
async
await
本身并不意味着多线程。但是,
Task.Run
会执行

因此,当前代码在线程池线程上显式阻塞:

private async void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
  // ** On the UI thread **
  Console.WriteLine("Caller threadId = " + Thread.CurrentThread.ManagedThreadId);
  await Task.Run(() =>
  {
    // ** On a thread pool thread **
    Console.WriteLine("working asynchronously on thread: " + Thread.CurrentThread.ManagedThreadId);
    Thread.Sleep(5);
  });
  // ** On the UI thread **
}
请注意,导致指定代码在线程池线程上运行的是
任务。运行
-而不是
等待

如果使用纯
异步
/
等待
而不使用
任务。运行
,您将看到所有代码都在UI线程上运行:

private async void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
  // ** On the UI thread **
  Console.WriteLine("Caller threadId = " + Thread.CurrentThread.ManagedThreadId);
  await DoNothingAsync();
  // ** On the UI thread **
}

private async Task DoNothingAsync()
{
  // ** On the UI thread **
  Console.WriteLine("working asynchronously on thread: " + Thread.CurrentThread.ManagedThreadId);
  await Task.Delay(5); // Task.Delay is the asynchronous version of Thread.Sleep
  // ** On the UI thread **
}

@阿列克谢列文科夫:是的。@Hans,你的假设是错误的<代码>任务。运行
正文保证在另一个线程上运行。它不需要是“新”线程,但仍然是“另一个”。或者在非常罕见的情况下(不是在你的情况下),即使是相同的,但以后。简而言之,
Task.Run
async
方法无关-它所做的只是“将要在线程池上运行的指定工作排队,并返回表示该工作的任务对象”-请参阅。是的,您可以-如果您只是通过等待其他异步方法来使用它们。我建议您阅读Stephen Toub的博客(从Ron Beyer提供的链接开始),以及“
async
方法可以在当前线程上运行”——是的,但是以
task.run()开始的任务不能运行(正如Ivan所说)。“您能在没有
Task.Run()
的情况下编写
async
方法吗?”--绝对可以。我甚至猜想大多数
async
都没有使用
Task.Run()
。一个非常常见的异步场景是I/O,在当前线程上运行的
async
I/O方法调用的一个示例是,如果I/O可以立即完成(例如,操作系统缓冲区已经准备好了要返回的数据)。方法本身中的代码可能会在当前线程中运行(取决于代码在方法中的位置以及当前同步上下文是什么);但是当它在等待其他东西时,它根本就没有运行,不管是在当前线程中还是在其他线程中。