首页 新闻 会员 周边

Task的Await在winfrom和控制台程序下为什么有不同的表现

0
悬赏园豆:100 [已解决问题] 解决于 2022-01-10 12:45

在控制台程序里面如下代码:

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 才可以获取结果。想知道为什么会出现这样的问题?

Tlink的主页 Tlink | 初学一级 | 园豆:116
提问于:2022-01-08 18:25
< >
分享
最佳答案
0

死锁了,给每个await的函数后面加上.ConfigureAwait(false) 就能解决问题
asp.net也会有个问题

收获园豆:100
WmW | 菜鸟二级 |园豆:424 | 2022-01-10 10:19
其他回答(2)
0

控制台程序只有一个线程,不存在线程切换问题

dudu | 园豆:30948 (高人七级) | 2022-01-08 19:13

这种死锁的根本原因是 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/

支持(0) 反对(0) Tlink | 园豆:116 (初学一级) | 2022-01-09 11:40

await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length).ConfigureAwait(false);

只需要将之前的方法后面改成这样就可以了:加一个ConfigureAwait(false);

支持(0) 反对(0) Tlink | 园豆:116 (初学一级) | 2022-01-09 13:18
0
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等等问题。

花飘水流兮 | 园豆:13567 (专家六级) | 2022-01-09 21:58
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册