【发布时间】:2018-05-02 11:48:10
【问题描述】:
预期结果:
TestAsync被UI线程调用,工作线程执行LongTask。
实际结果:
ui线程执行一切
测试:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// [...]
_fab = root.FindViewById<FloatingActionButton>(...);
_fab.Click += ((sender, v) => TestAsync("fab"));
// [...]
}
private async void TestAsync(string origin)
{
await LongTask();
}
private async Task LongTask()
{
while (true) { } // Thread should hung here
}
结果:UI 冻结。
测试 2: 为了确保 UI 正在执行一切,我做了一个网络操作(在 Android 的 UI 线程中是不允许的)
public async Task<int> Network(string s)
{
URL url = new URL("http://www.randomtext.me/api/");
Java.IO.BufferedReader reader = new Java.IO.BufferedReader(new Java.IO.InputStreamReader(url.OpenStream()));
int count = 0;
string str;
while ((str = reader.ReadLine()) != null) {
count += str.Length;
}
reader.Close();
await Task.Delay(3000); // To make sure this method is compiled as async even though it isn't necessary
return count;
}
结果:NetworkOnMainThreadException.
问题:
为什么LongTask 和Network 方法不在工作线程中执行?那么await/async 是什么?
谢谢。
【问题讨论】:
-
异步方法中的上下文切换(可能)发生在第一个
await。它可能根本不会发生,但在第一次await之前,一切肯定会在您调用它的同一线程上运行。 -
好吧,我无法在 cmets 中正确解释它,除此之外,这个网站上已经解释了数百次。例如,将您的第二种方法更改为使用
while ((str = await reader.ReadLineAsync()) != null)。 -
您可以从中提取的主要内容是:如果您对
await没有任何东西,请不要使用async。它并非旨在将您的工作转移到后台线程,因为您有Task.Run和类似的构造。 -
是的,在这种情况下使用
reader.ReadLineAsync是不够的,因为url.OpenStream会抛出异常并且它没有等效的异步版本。由于我上面描述的原因,它抛出了这个异常——在第一次等待之前,你肯定在 UI 线程上(你可能也在 UI 线程上)。因此,不要使用 java 类 - 使用 .NET 类,例如HttpWebRequest、HttpClient等。或者使用 android 特定的后台任务。如果您想从 DB 中读取数据 - 只需这样做即可进行测试。 -
作为指南 - 它只是 async\await 工作原理的核心,您可以阅读任何相关指南。例如文档:docs.microsoft.com/en-us/dotnet/csharp/async。在这个网站上也有很多关于 async\await 不同方面的不同问题。
标签: c# android xamarin xamarin.android async-await