C#异步等待 - Gui冻结失焦时

时间:2013-11-19 10:13:55

标签: winforms sorting async-await

我最近决定看一下C#5的新async / await功能。出于演示目的,我编写了一个Windows窗体应用程序,允许用户获取整数数组并使用他们选择的排序例程对它们进行排序。与程序一起,我提供了五个测试.dat文件,每个文件包含从32,000个整数开始的未排序数据,每个后续文件的大小加倍。程序将文件中的数据读入一个准备排序的int数组。

我遇到的问题是GUI冻结并变得完全没有响应。但是,只有在GUI本身放置在后台时才会冻结。即,我看不到它,因为我可能正在忙着浏览互联网等。它可能不会立即冻结。有时候我可能会把它重新放回焦点但它仍然有效,但最终它会失败。如果我始终保持程序焦点,GUI将保持完全响应。我可以将它拖过屏幕。我可以最小化并最大化它。一个选框样式进度条动画,我可以在结果表单显示已用的排序时间之前继续单击按钮等。

现在我想补充一点,我不认为这是排序算法本身的问题。我的理由是,当GUI保持关注时,它工作正常。我还有一整套单元测试,验证每个算法是否正常工作。经过的时间似乎是一个问题,因为更快的算法(如堆或快速排序)可以处理非常大的数据集而不会导致冻结。最后,我尝试在表单级别(我等待任务)和任务本身内捕获异常,但似乎没有异常抛出异常。

在过去的两天里,我只是看了async / await,我绝不是专家,但我最初的想法是,它可能与重新获得焦点时重新关联。有人可以帮忙吗?以下是协助的代码部分: -

示例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窗体Click事件处理程序的调用: -

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);   
        }

1 个答案:

答案 0 :(得分:0)

异步代码似乎没有任何问题。在排序后引发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();
    }