无法调试使用并发async / await的程序

时间:2012-10-26 12:47:05

标签: c# .net debugging .net-4.5 async-await

观察on this program

  • 通过此程序缓慢按F11不会显示每次执行ProcessURL()

  • 通过此程序快速按F11可显示ProcessURL()的更多执行

  • 在ProcessURL中使用Thread.Sleep(3000);会导致MainUI线程挂起约30秒。没有UI重绘,取消按钮不可用。

需要:

  • 我想逐步完成ProcessURL的每次执行,或者使用本机Visual Studio工具或开源添加项将其可视化为

enter image description here

代码

Download available here

namespace ProcessTasksAsTheyFinish
{
    public partial class MainWindow : Window
    {
        // Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();

            // Instantiate the CancellationTokenSource.
            cts = new CancellationTokenSource();

            try
            {
                await AccessTheWebAsync(cts.Token);
                resultsTextBox.Text += "\r\nDownloads complete.";
            }
            catch (OperationCanceledException)
            {
                resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
            }
            catch (Exception)
            {
                resultsTextBox.Text += "\r\nDownloads failed.\r\n";
            }

            cts = null;
        }


        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }
        }


        async Task AccessTheWebAsync(CancellationToken ct)
        {
            HttpClient client = new HttpClient();

            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            // ***Create a query that, when executed, returns a collection of tasks.
            IEnumerable<Task<int>> downloadTasksQuery =
                from url in urlList select ProcessURL(url, client, ct);

            // ***Use ToList to execute the query and start the tasks. 
            List<Task<int>> downloadTasks = downloadTasksQuery.ToList();

            // ***Add a loop to process the tasks one at a time until none remain.
            while (downloadTasks.Count > 0)
            {
                    // Identify the first task that completes.
                    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);

                    // ***Remove the selected task from the list so that you don't
                    // process it more than once.
                    downloadTasks.Remove(firstFinishedTask);

                    // Await the completed task.
                    int length = await firstFinishedTask;
                    resultsTextBox.Text += String.Format
                        ("\r\nLength of the download:  {0}", length);
            }
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "http://msdn.microsoft.com",
                "http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "http://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "http://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "http://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "http://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "http://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)
        {
            // GetAsync returns a Task<HttpResponseMessage>. 
            HttpResponseMessage response = await client.GetAsync(url, ct);
            // Retrieve the website contents from the HttpResponseMessage.
            byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

            return urlContents.Length;
        }
    }
}

1 个答案:

答案 0 :(得分:5)

假设您在<{em} Thread.Sleep调用之前调用了await ,那么将UI线程锁定是完全合理的:您阻止了它。您的ProcessURL方法将同步执行,直到您针对尚未完成的内容点击第一个await表达式。当它到达那里时,它将附加一个延续,然后返回。

因此,如果您在等待之前进行了Thread.Sleep调用,那么当您执行LINQ查询时(当您调用ToList时,您将连续7次调用该方法,在UI中休眠每次线程3秒。当发生这种情况时,用户界面将被锁定。如果你在Thread.Sleep之后放置await ,那么用户界面仍将被锁定相同的时间,但是以小爆发。

Thread.Sleep的异步等效项是使用Task.Delay

await Task.Delay(3000);

这将基本上立即返回,附加一个将在3秒内触发的延续。

(我不知道调试问题 - 我不会尝试将调试到大部分语句......我不清楚你到底想要实现什么或者为什么。ProcessURL中的断点应该会被每个网址命中。)