为何在使用async、await和Task.Run()时会收到Visual Studio编译警告CS1998?

huangapple go评论66阅读模式
英文:

Why am I receiving CS1998 visual studio compilation warning when using async, await and Task.Run()?

问题

The instructor's usage of the async keyword in this code might be misleading. Removing the async keyword before the lambda expression inside Task.Run is actually correct, as it prevents the warning CS1998 about running synchronously. So, your understanding is correct, and it's not an error to remove the async keyword in this context.

In summary, you can safely remove the async keyword before the lambda expressions inside Task.Run without introducing any bad habits or patterns. This will help avoid the warning and ensure that the code runs asynchronously as intended.

英文:

I'm taking a Udemy class and they are trying to explain threading. The full error I am receiving is

Warning CS1998 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

I have reviewed many of the current SO answers and they don't quite match my situation at least to the level of my current understanding.

The learning project is a simple WPF with 2 buttons that when clicked download files and a counter for the current time. The goal is to ensure that a user can interact with the UI while downloading the files. The counter shows that the UI is updating.

The instructor states

> "So here we created a asynchronous task which does that in the background, and once that is done, we can go ahead with the rest. So it's not going to freeze our main thread, our UI is still going to work properly..."

He includes the async keyword in the body of both button event handlers even though the compiler warns that it will run synchronously which does not appear to be true. The code runs fine if the async keyword is removed before the lambda expression. Both methods contain this code:

await Task.Run(async () => {...});

Code follows.

MainWindow.xaml.cs

using System;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

using System.Windows.Threading;

namespace ThreadTasks_WPF_question
{

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += timer_Tick;
            timer.Start();
        }
        void timer_Tick(object sender, EventArgs e)
        {
            MyLabel1.Content = DateTime.Now.ToLongTimeString();
        }
        private async void MyButton1_Click(object sender, RoutedEventArgs e)
        {
            
            MyButton1.Content = "MyButton1 Downloading";
            Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} before await button 1");
            
             // async below causes warning
            await Task.Run(async () =>
            {

                Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} during button 1");
                HttpClient webClient2 = new HttpClient();
                string userAgent2 = @"Mozilla/5.0 (compatible; word-word-name.name@domain.com)";
                webClient2.DefaultRequestHeaders.Add("User-Agent", userAgent2);
                string html = webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip").Result;


            });

            Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} after await button 1");
            MyButton1.Content = "MyButton1 Done";
        }
        private async void MyButton2_Click(object sender, RoutedEventArgs e)
        {
            MyButton2.Content = "MyButton2 Downloading";
            Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} before await button 2");
          
            // async below causes warning
            await Task.Run(async () =>
            {
                Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} during button 2");
                HttpClient webClient = new HttpClient();
                string userAgent2 = @"Mozilla/5.0 (compatible; word-word-name.name@domain.com)";
                webClient.DefaultRequestHeaders.Add("User-Agent", userAgent2);
                string html = webClient.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip").Result;
            });

            Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} after await button 2");
            MyButton2.Content = "MyButton2 Done";
        }
    }
}

MainWindow.xaml

<Window x:Class="ThreadTasks_WPF_question.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ThreadTasks_WPF_question"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Name="MyButton1" HorizontalAlignment="Left" Margin="275,114,0,0" VerticalAlignment="Top" Height="70" Width="210" Click="MyButton1_Click">
            <Button Content="Button 1"/>
        </Button>
        <Button Name="MyButton2" HorizontalAlignment="Left" Margin="275,234,0,0" VerticalAlignment="Top" Height="70" Width="210" Click="MyButton2_Click">
            <Button Content="Button 2"/>
        </Button>
        <Label Name="MyLabel1" Content="Label" HorizontalAlignment="Left" Margin="360,344,0,0" VerticalAlignment="Top"/>

    </Grid>
</Window>```

Was this an error by the instructor? The button click methods already had an async modifier. I'm not understanding what was intended and I would prefer not to learn bad habits or patterns.

答案1

得分: 4

问题出在这一行:

string html = webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip").Result;

应该是:

string html = await webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip");

但实际上在这里根本不应该使用 Task.Run。没有理由这样做。你的代码可以简单写成这样:

private async void MyButton1_Click(object sender, RoutedEventArgs e)
{
	
	MyButton1.Content = "MyButton1 正在下载";
	Debug.WriteLine($"线程编号 {Thread.CurrentThread.ManagedThreadId} 在按钮 1 await 之前");
	
	Debug.WriteLine($"线程编号 {Thread.CurrentThread.ManagedThreadId} 在按钮 1 进行中");
	HttpClient webClient2 = new HttpClient();
	string userAgent2 = @"Mozilla/5.0 (compatible; word-word-name.name@domain.com)";
	webClient2.DefaultRequestHeaders.Add("User-Agent", userAgent2);
	string html = await webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip");


	Debug.WriteLine($"线程编号 {Thread.CurrentThread.ManagedThreadId} 在按钮 1 await 之后");
	MyButton1.Content = "MyButton1 完成";
}

请注意,Microsoft 的建议是将 HttpClient 设为应用程序内的静态字段或属性,而不是每次使用时重新创建。对于其他读者:如果你正在创建 ASP.NET Core 应用程序,应该使用 HttpClientFactory

英文:

The problem is this line:

string html = webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip").Result;

It should be:

string html = await webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip");

But really you shouldn't be using Task.Run at all here. There's no reason to do so. Your code can be simply written like this:

private async void MyButton1_Click(object sender, RoutedEventArgs e)
{
	
	MyButton1.Content = "MyButton1 Downloading";
	Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} before await button 1");
	
	Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} during button 1");
	HttpClient webClient2 = new HttpClient();
	string userAgent2 = @"Mozilla/5.0 (compatible; word-word-name.name@domain.com)";
	webClient2.DefaultRequestHeaders.Add("User-Agent", userAgent2);
	string html = await webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip");


	Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} after await button 1");
	MyButton1.Content = "MyButton1 Done";
}

Note that Microsoft's recommendation is that HttpClient be a static field or property within your application, not recreated each time you use it. For others reading: if you're making an ASP.NET Core application, you should use HttpClientFactory.

答案2

得分: 1

Remove the async keyword from the delegate that you pass to Task.Run, i.e., change from await Task.Run(async () => { ... } to await Task.Run(() => { ... }.

Or better yet, await the GetStringAsync method:

await Task.Run(() =>
{
    Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} during button 1");
    HttpClient webClient2 = new HttpClient();
    string userAgent2 = @"Mozilla/5.0 (compatible; word-word-name.name@domain.com)";
    webClient2.DefaultRequestHeaders.Add("User-Agent", userAgent2);
    string html = webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip").Result;
});
英文:

Either remove the async keyword from the delegate that you pass to Task.Run , i.e. change from await Task.Run(async () => { ... } to await Task.Run(() => { ... }.

Or better yet, await the GetStringAsync method:

await Task.Run(async () =>
{
    Debug.WriteLine($"Thread No. {Thread.CurrentThread.ManagedThreadId} during button 1");
    HttpClient webClient2 = new HttpClient();
    string userAgent2 = @"Mozilla/5.0 (compatible; word-word-name.name@domain.com)";
    webClient2.DefaultRequestHeaders.Add("User-Agent", userAgent2);
    string html = async webClient2.GetStringAsync("http://ipv4.download.thinkbroadband.com/20MB.zip");
});

huangapple
  • 本文由 发表于 2023年5月22日 09:54:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76302632.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定