我最近决定研究一下C#5的新异步/等待特性。出于演示目的,我编程了一个Windows窗体应用程序,允许用户获取一个整数数组,并使用他们选择的排序例程对其进行排序。在这个程序中,我提供了五个test.dat文件,每个文件都包含未排序的数据,从32000个整数开始,每个后续文件的大小都会翻倍。该程序将文件中的数据读取到一个int数组中,以便进行排序
我遇到的问题是GUI冻结并且变得完全没有响应。但是,只有将GUI本身放在后台时,它才会冻结。也就是说,我看不见它,因为我可能正忙于浏览互联网等。它可能不会立即冻结。有时我可能会让它重新聚焦,它仍然有效,但最终会失败。如果我让程序始终处于焦点状态,GUI将保持完全响应。我可以在屏幕上拖动它。我可以最小化和最大化它。在结果表显示经过的排序时间之前,一个字幕式的进度条会产生动画,我可以继续点击按钮等。
现在我要补充一点,我不认为这是排序算法本身的问题。我的理由是,当GUI处于焦点时,它可以很好地工作。我还有一整套单元测试来验证每个算法是否正常工作。运行时间似乎是一个问题,因为Heap或Quick sort等更快的算法可以处理非常大的数据集,而不会导致冻结。最后,我尝试在等待Task的表单级别和任务本身内部捕获异常,但似乎没有抛出异常。
在过去的两天里,我只研究了async/await,我决不是一个专家,但我最初的想法是,当重新聚焦时,它可能与GUI重新绘制有关。有人能帮忙吗?以下是代码的帮助部分:-
示例SelectionSort例程:-
public SelectionSort(IStopwatch stopwatch) : base(stopwatch){}
public override async Task<int[]> SortAsync(int[] data, CancellationToken cancelToken)
{
OnStarted();
Stopwatch.Start();
await Task.Run(() =>
{
int i, j;
int min, temp;
for (i = 0; i < data.Length - 1; i++)
{
if (cancelToken.IsCancellationRequested)
return;
min = i;
for (j = i + 1; j < data.Length; j++)
{
if (data[j] < data[min])
min = j;
}
temp = data[i];
data[i] = data[min];
data[min] = temp;
}
}, cancelToken);
Stopwatch.Stop();
OnCompleted(new SortCompleteEventArgs(Stopwatch.ElapsedMilliseconds, data.Length, cancelToken.IsCancellationRequested));
return data;
}
处理每个分拣例程的分拣机上下文(策略模式):-
public class SorterContext
{
private readonly SortRoutine _sortRoutine;
public SorterContext(SortRoutine sortRoutine)
{
_sortRoutine = sortRoutine;
}
public async Task<int[]> Sort(int[] dataToSort, CancellationToken cancellationToken)
{
if(dataToSort == null)
throw new ArgumentNullException("dataToSort");
if(dataToSort.Length == 1)
throw new ArgumentOutOfRangeException("dataToSort");
int[] result = await _sortRoutine.SortAsync(dataToSort, cancellationToken);
return result;
}
来自Windows窗体单击事件处理程序的调用:-
private async void StartSorting_Click(object sender, EventArgs e)
{
if (_lBoxSelectedFiles.Items.Count == 0)
{
MessageBox.Show("Select data to sort");
return;
}
_btnCancelSort.Enabled = true;
if (_comboBxAlgorithm.SelectedValue.Equals("SelectSort"))
{
var selectSort = new SelectSort(new SortStopwatch());
selectSort.Completed += DisplaySortResults;
_sorter = new SorterContext(selectSort);
StartProgressBar();
await _sorter.Sort(_dataToSort, _cancelTokenSrcWrapper.Token);
}
异步代码似乎没有任何问题。当排序后引发OnComplete事件时,主窗体中的处理程序会实例化一个新的结果窗体并显示它。当窗体失去焦点时,这会失败。我正在调查为什么会出现这种情况,以及这是否是一个深思熟虑的框架设计决定。无论如何,我只是从处理程序中调用了方法Activate()。这使应用程序成为焦点,然后我实例化了新表单,它运行良好。至少对我来说是这样。例如:-
private void DisplaySortResults(object sender, SortCompleteEventArgs e)
{
Activate();
_btnCancelSort.Enabled = false;
_progressBar.Style = ProgressBarStyle.Continuous;
var sortResults = new SortResults();
sortResults.BuildResults(e);
sortResults.SetCompletedValues(e);
sortResults.ShowDialog();
}