异步&;在C#中等待:我是否必须将整个调用链更改为Void方法?

异步&;在C#中等待:我是否必须将整个调用链更改为Void方法?,c#,multithreading,asynchronous,async-await,C#,Multithreading,Asynchronous,Async Await,我对C#的整个异步/等待主题是完全陌生的。尽管回答了无数的问题和教程,但我似乎仍然无法理解如何使用async/await。我想要实现的就是wpf应用程序在等待昂贵的方法调用完成时返回到UI上渲染动画-在我下面的示例中,这将是GetLinesAsync(),或者更具体地说,昂贵的函数(): void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(GetAMessage()); } string GetAM

我对C#的整个异步/等待主题是完全陌生的。尽管回答了无数的问题和教程,但我似乎仍然无法理解如何使用async/await。我想要实现的就是wpf应用程序在等待昂贵的方法调用完成时返回到UI上渲染动画-在我下面的示例中,这将是
GetLinesAsync()
,或者更具体地说,
昂贵的函数()

void Button_Click(object sender, RoutedEventArgs e) {
    MessageBox.Show(GetAMessage());
}

string GetAMessage() {
    string ret = "";
    foreach(string s in GetLinesAsync().Result)
        ret += $"{s}\n";
    return ret;
}

async Task<List<string>> GetLinesAsync() {
    List<string> ret = new List<string>();
    string[] ExpensiveResult = null;
    if (await Task.Run(() => expensive_function(ref ExpensiveResult)) == expensive_stat.SUCCESS && ExpensiveResult != null)
    {
        foreach(string s in ExpensiveResult)
            ret.Add(s);
    }
void按钮\u单击(对象发送器,路由目标){
Show(GetAMessage());
}
字符串GetAMessage(){
字符串ret=“”;
foreach(GetLinesAsync().Result中的字符串s)
ret+=$“{s}\n”;
返回ret;
}
异步任务GetLinesAsync(){
List ret=新列表();
字符串[]ExpensiveResult=null;
if(wait Task.Run(()=>昂贵的_函数(ref ExpensiveResult))==昂贵的_stat.SUCCESS&&ExpensiveResult!=null)
{
取证(ExpensiveResult中的字符串s)
ret.Add(s);
}
这样编写,应用程序完全冻结,即使
昂贵的函数()
不会花那么长时间执行。除此之外,我不确定为什么会发生这种情况,以下是我从阅读关于wait/async的教程和说明中了解到的内容,特别是说您只能等待返回void或Task的方法:

foreach(GetLinesAsync().Result中的字符串s)
实际上应该说
foreach(wait GetLinesAsync().Result中的字符串s)
-但是我必须标记
GetMessages()
async,然后我必须用
MessageBox.Show(wait GetMessages())替换
MessageBox.Show(GetAMessage())
并标记
按钮\u单击()
异步


换句话说:等待性将在调用堆栈中逐渐上升,直到它到达一个void方法。好吧,但这不可能,不是吗?如果在其他一些场景中,甚至没有一个敏感的void方法可以上升呢?

代码被阻塞的原因是因为您在这里阻塞了它

foreach(string s in GetLinesAsync().Result)
你是说UI线程等待我昂贵的任务完成

asyn
c自由流动

  • 在Windows窗体和WPF中,
    async
    /
    await
    具有方便的属性,即在等待的异步操作完成时返回UI线程

  • 对void方法的
    async
    支持是专门为支持事件处理程序而添加的。但是,请确保处理异常,因为void将不被观察到

所以你可以这么做

注意:这是一个基于您的代码的极其简化和净化的示例

public async void Button_Click(object sender, RoutedEventArgs e)
{
   try
   {
      MessageBox.Show(await GetAMessage());
   }
   catch (Exception exception)
   {
      // log the nelly out of it
      // or display message
   }

}

public async Task<string> GetAMessage()
{
   var list = await GetLinesAsync();

   return string.Join("\n", list);
}

public List<string> ExpensiveFunction()
{
   // ...
   return new List<string>();
}


public async Task<List<string>> GetLinesAsync()
{
   var result = Task.Run(() => ExpensiveFunction());
   return await result;
}
public async void按钮\u单击(对象发送方,路由目标)
{
尝试
{
Show(等待GetAMessage());
}
捕获(异常)
{
//把耐莉从里面赶走
//或显示消息
}
}
公共异步任务GetAMessage()
{
var list=await GetLinesAsync();
返回字符串。连接(“\n”,列表);
}
公共列表费用函数()
{
// ...
返回新列表();
}
公共异步任务GetLinesAsync()
{
var result=Task.Run(()=>ExpensiveFunction());
返回等待结果;
}

代码被阻塞的原因是因为您在这里阻塞了它

foreach(string s in GetLinesAsync().Result)
你是说UI线程等待我昂贵的任务完成

asyn
c自由流动

  • 在Windows窗体和WPF中,
    async
    /
    await
    具有方便的属性,即在等待的异步操作完成时返回UI线程

  • 对void方法的
    async
    支持是专门为支持事件处理程序而添加的。但是,请确保处理异常,因为void将不被观察到

所以你可以这么做

注意:这是一个基于您的代码的极其简化和净化的示例

public async void Button_Click(object sender, RoutedEventArgs e)
{
   try
   {
      MessageBox.Show(await GetAMessage());
   }
   catch (Exception exception)
   {
      // log the nelly out of it
      // or display message
   }

}

public async Task<string> GetAMessage()
{
   var list = await GetLinesAsync();

   return string.Join("\n", list);
}

public List<string> ExpensiveFunction()
{
   // ...
   return new List<string>();
}


public async Task<List<string>> GetLinesAsync()
{
   var result = Task.Run(() => ExpensiveFunction());
   return await result;
}
public async void按钮\u单击(对象发送方,路由目标)
{
尝试
{
Show(等待GetAMessage());
}
捕获(异常)
{
//把耐莉从里面赶走
//或显示消息
}
}
公共异步任务GetAMessage()
{
var list=await GetLinesAsync();
返回字符串。连接(“\n”,列表);
}
公共列表费用函数()
{
// ...
返回新列表();
}
公共异步任务GetLinesAsync()
{
var result=Task.Run(()=>ExpensiveFunction());
返回等待结果;
}

换句话说:等待性会在调用堆栈中爬行,直到它到达一个void方法
是的,这就是发生的情况,以及它在现实世界中的工作方式。让它传播,不要阻止它。尽管它并不总是根目录下的一个
void
方法。引入了C#7,它允许您拥有一个
公共静态异步方法ask Main(字符串[]参数)
方法作为应用程序的入口点。
换句话说:等待性将沿着调用堆栈向上爬行,直到它到达一个无效的方法
是的,这就是发生的情况,以及它在现实世界中的工作方式。让它传播,不要阻止它。尽管在根目录下它并不总是一个
无效的
方法允许您使用
公共静态异步任务Main(string[]args)
方法作为应用程序的入口点。这也是Microsoft在其文章中所说的-这对于无效返回事件处理程序(如
按钮单击()
)很好。是否有其他方法定义回调,比如说“运行
昂贵的函数()
在单独的任务中,继续,完成后运行
callback()
“@draconigen如果不想使用异步awat,只需关闭
任务
线程
,并在要显示消息或更新ui时封送回ui线程。
控件。BeginInvoke