在控制台程序里面如下代码:
static void Main(string[] args)
{
var fileName = Environment.CurrentDirectory + @"\test.txt";
var content = GetFileContent(fileName);
Console.WriteLine("文件内容:" + content.Result);
Console.ReadKey();
}
public static async Task<string> GetFileContent(string fileName)
{
using (FileStream SourceStream = File.Open(fileName, FileMode.Open))
{
var result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
return System.Text.Encoding.ASCII.GetString(result);
}
}
在控制台上能正常运行,得到想要的结果,同样在winfrom代码如下:
private void button1_Click(object sender, EventArgs e)
{
var fileName = Environment.CurrentDirectory + @"\test.txt";
var content = GetFileContent(fileName);
rtxtInfo.Text = content.Result;
}
public async Task<string> GetFileContent(string fileName)
{
using (FileStream SourceStream = File.Open(fileName, FileMode.Open))
{
var result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
return System.Text.Encoding.ASCII.GetString(result);
}
}
方法一模一样,在winfrom里面这样写,得不到结果,处于一直在等待执行状态,导致界面假死,同样的操作在控制台缺可以.
在winfrom里面需要改成异步加await 才可以获取结果。想知道为什么会出现这样的问题?
死锁了,给每个await的函数后面加上.ConfigureAwait(false) 就能解决问题
asp.net也会有个问题
控制台程序只有一个线程,不存在线程切换问题
这种死锁的根本原因是 await 处理上下文的方式。 默认情况下,当等待未完成的 Task 时,会捕获当前“上下文”,在 Task 完成时使用该上下文恢复方法的执行。 此“上下文”是当前 SynchronizationContext(除非它是 null,这种情况下则为当前 TaskScheduler)。 GUI 和 ASP.NET 应用程序具有 SynchronizationContext,它每次仅允许一个代码区块运行。 当 await 完成时,它会尝试在捕获的上下文中执行 async 方法的剩余部分。但是该上下文已含有一个线程,该线程在(同步)等待 async 方法完成。 它们相互等待对方,从而导致死锁。
请注意,控制台应用程序不会形成这种死锁。 它们具有线程池 SynchronizationContext 而不是每次执行一个区块的 SynchronizationContext,因此当 await 完成时,它会在线程池线程上安排 async 方法的剩余部分。该方法能够完成,并完成其返回任务,因此不存在死锁。 当程序员编写测试控制台程序,观察到部分异步代码按预期方式工作,然后将相同代码移动到 GUI 或 ASP.NET 应用程序中会发生死锁,此行为差异可能会令人困惑
ref:https://blog.csdn.net/nacl025/article/details/9163495/
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length).ConfigureAwait(false);
只需要将之前的方法后面改成这样就可以了:加一个ConfigureAwait(false);
static void Main(string[] args) { var fileName = Environment.CurrentDirectory + @"\test.txt"; GetFileContentAsync(fileName).Continue(t=> {Console.WriteLine("文件内容:" + t.Result);}); //winForm同理 Console.ReadKey(); } public static async Task<string> GetFileContentAsync(string fileName) { using (FileStream SourceStream = File.Open(fileName, FileMode.Open)) { var result = new byte[SourceStream.Length]; await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length); return System.Text.Encoding.ASCII.GetString(result); } }
这玩意儿就是为了易用【后面会有示例说明】和轻量【自己去实现一个线程池就会明白为什么非要搞个Task(以前可没这玩意儿)】 多线程 而设计。知其所以然,你才能用得更溜~~
【给你加上Async,你看多了ms等等甚至第三方,大家都会按照这种“习惯”来写】
写法可以多种,关键在于理解。不用await和asynce关键字同样可以实现;
那么在main中应该这么写:
var asyncResult = GetFileContentAsync();
while(!asynceReult.IsComplete)Thread.Sleep(0);//你看是不是 await 会很简单方便(貌似记得是有个Task成员IsComplete哈,反正就是这个道理),而且线程很多的时候这样写不仅简单,也不容易造成 第一种Continue这种栈式的面条方式;
Console.WriteLine(asyncResult.Result);
你自己去实现了线程池,你再回头自己实现一个Task,你就理解为什么Task要带Exception等等问题。