根据我的理解,async和await所做的主要事情之一是使代码易于编写和阅读-但使用它们是否等于生成后台线程来执行长时间的逻辑?
我目前正在尝试最基本的例子。我内联添加了一些注释。你能给我解释一下吗?
// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
Task<int> access = DoSomethingAsync();
// task independent stuff here
// this line is reached after the 5 seconds sleep from
// DoSomethingAsync() method. Shouldn't it be reached immediately?
int a = 1;
// from my understanding the waiting should be done here.
int x = await access;
}
async Task<int> DoSomethingAsync()
{
// is this executed on a background thread?
System.Threading.Thread.Sleep(5000);
return 1;
}
除了其他答案,还有await (c#参考)
更具体地说,在包含的例子中,它解释了您的情况
下面的Windows窗体示例说明了await在
异步方法,WaitAsynchronouslyAsync。对比一下它的行为
方法使用waitsynchrontically的行为。没有等待
应用到任务的操作符,waitsynchronize同步运行
尽管在定义中使用了async修饰符,并且调用了
线程。睡在它的身体里。
private async void button1_Click(object sender, EventArgs e)
{
// Call the method that runs asynchronously.
string result = await WaitAsynchronouslyAsync();
// Call the method that runs synchronously.
//string result = await WaitSynchronously ();
// Display the result.
textBox1.Text += result;
}
// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
await Task.Delay(10000);
return "Finished";
}
// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
// Add a using directive for System.Threading.
Thread.Sleep(10000);
return "Finished";
}
当使用async和await时,编译器在后台生成一个状态机。
下面是一个例子,我希望我能解释一些正在发生的高级细节:
public async Task MyMethodAsync()
{
Task<int> longRunningTask = LongRunningOperationAsync();
// independent work which doesn't need the result of LongRunningOperationAsync can be done here
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation
{
await Task.Delay(1000); // 1 second delay
return 1;
}
好的,这里发生了什么:
Task<int> longRunningTask = LongRunningOperationAsync();开始执行LongRunningOperation
独立的工作完成了,假设主线程(线程ID = 1),然后等待longRunningTask到达。
现在,如果longRunningTask还没有完成,它仍在运行,MyMethodAsync()将返回到它的调用方法,因此主线程不会被阻塞。当longRunningTask完成时,来自ThreadPool的线程(可以是任何线程)将返回到MyMethodAsync()之前的上下文中并继续执行(在这种情况下将结果打印到控制台)。
第二种情况是longRunningTask已经完成执行,结果可用。当到达await longRunningTask时,我们已经有了结果,所以代码将继续在同一线程上执行。(在本例中将结果打印到控制台)。当然,对于上面的例子,情况并非如此,其中涉及到Task.Delay(1000)。
在一个简单的控制台程序中显示上述解释:
class Program
{
static void Main(string[] args)
{
TestAsyncAwaitMethods();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
public async static void TestAsyncAwaitMethods()
{
await LongRunningMethod();
}
public static async Task<int> LongRunningMethod()
{
Console.WriteLine("Starting Long Running method...");
await Task.Delay(5000);
Console.WriteLine("End Long Running method...");
return 1;
}
}
输出为:
Starting Long Running method...
Press any key to exit...
End Long Running method...
因此,
Main通过TestAsyncAwaitMethods启动长时间运行的方法。这立即返回,而不停止当前线程,我们立即看到'按任何键退出'消息
在此期间,LongRunningMethod一直在后台运行。一旦它完成,来自Threadpool的另一个线程拾取该上下文并显示最终消息
因此,没有线程被阻塞。
回答你的第二个问题-何时使用async -这里有一个相当简单的方法,我们使用:
长时间运行的I/O绑定任务,运行时间超过50ms -使用异步。
长时间运行的cpu绑定任务——使用并行执行、线程等。
解释:当你在做I/O工作时——发送网络请求,从磁盘读取数据等——实际的工作是由“外部”硅(网卡,磁盘控制器等)完成的。一旦工作完成,I/O设备驱动程序将“ping”回操作系统,操作系统将执行你的延续代码,回调等。在此之前,CPU可以自由地做自己的工作(作为奖励,你还可以释放一个线程池线程,这对web应用程序的可伸缩性来说是一个非常好的奖励)
附注:50ms阈值是MS的推荐值。否则,异步所增加的开销(创建状态机、执行上下文等)会消耗掉所有的好处。现在找不到MS的原始文章,但这里也提到了https://www.red-gate.com/simple-talk/dotnet/net-framework/the-overhead-of-asyncawait-in-net-4-5/
下面是通过打开对话框读取excel文件的代码,然后使用async和等待运行异步代码,从excel逐行读取并绑定到网格
namespace EmailBillingRates
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
lblProcessing.Text = "";
}
private async void btnReadExcel_Click(object sender, EventArgs e)
{
string filename = OpenFileDialog();
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
try
{
Task<int> longRunningTask = BindGrid(xlRange);
int result = await longRunningTask;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
finally
{
//cleanup
// GC.Collect();
//GC.WaitForPendingFinalizers();
//rule of thumb for releasing com objects:
// never use two dots, all COM objects must be referenced and released individually
// ex: [somthing].[something].[something] is bad
//release com objects to fully kill excel process from running in the background
Marshal.ReleaseComObject(xlRange);
Marshal.ReleaseComObject(xlWorksheet);
//close and release
xlWorkbook.Close();
Marshal.ReleaseComObject(xlWorkbook);
//quit and release
xlApp.Quit();
Marshal.ReleaseComObject(xlApp);
}
}
private void btnSendEmail_Click(object sender, EventArgs e)
{
}
private string OpenFileDialog()
{
string filename = "";
OpenFileDialog fdlg = new OpenFileDialog();
fdlg.Title = "Excel File Dialog";
fdlg.InitialDirectory = @"c:\";
fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
fdlg.FilterIndex = 2;
fdlg.RestoreDirectory = true;
if (fdlg.ShowDialog() == DialogResult.OK)
{
filename = fdlg.FileName;
}
return filename;
}
private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
{
lblProcessing.Text = "Processing File.. Please wait";
int rowCount = xlRange.Rows.Count;
int colCount = xlRange.Columns.Count;
// dt.Column = colCount;
dataGridView1.ColumnCount = colCount;
dataGridView1.RowCount = rowCount;
for (int i = 1; i <= rowCount; i++)
{
for (int j = 1; j <= colCount; j++)
{
//write the value to the Grid
if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
{
await Task.Delay(1);
dataGridView1.Rows[i - 1].Cells[j - 1].Value = xlRange.Cells[i, j].Value2.ToString();
}
}
}
lblProcessing.Text = "";
return 0;
}
}
internal class async
{
}
}