通过此程序缓慢按F11不会显示每次执行ProcessURL()
通过此程序快速按F11可显示ProcessURL()
的更多执行
在ProcessURL中使用Thread.Sleep(3000);
会导致MainUI线程挂起约30秒。没有UI重绘,取消按钮不可用。
需要:
代码
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;
}
}
}
答案 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
中的断点应该会被每个网址命中。)