中止先前的异步调用并处置已使用的资源

时间:2014-12-03 07:45:05

标签: c# c#-4.0 devexpress async-await wpfdatagrid

我有一个负责渲染网格记录的类。

ObservableCollection<Dictionary<string, string>> gridData;
ObservableCollection<Dictionary<string, string>> GridData;
{
    get
    {
        return gridData;
    }
    set
    {
        this.gridData = value;
        NotifyPropertyChanged();
    }
}  

以上属性绑定到GridControl(Devexpress WPF)。

下面是异步方法,负责从后端服务呈现数据。

public async void RefresRecords()
{
    await GetRecords();
}

private async Task GetRecords()
{
    int offset = 0;
    int limit = 50;

    do
    {
        var dummyData = await GetRangeRecord(offset, limit);
        GridData.Add(dummyData);

        offset += limit;

    } while (GridData.Count() < 1000);
}

private Task<Dictionary<string, string>> GetRangeRecord(int offset, int limit)
{
    // This method does processing and returns records from offset to limit
    return new Task<Dictionary<string, string>>();
} 

从后端服务可以获得大量数据,但我想只显示前1000条记录。

记录将以每次调用50个记录的大小提取。当属性绑定时,UI将同时刷新。

现在考虑用例如下: -

步骤1: - 呼叫来自asyn方法&#34; RefresRecords&#34;。假设从后端服务获取了1000条记录。因此,此方法将循环20次,每个服务调用将显示50条记录。

步骤2: - 当Step1正在进行同步调用时,另一个调用是针对相同的异步方法&#34; RefreshRecords&#34;。

结果: - 在第2步之后,网格将加载来自Step2的获取数据的数据以及Step1的某些部分,因为在随后的两个asyn方法调用中相同的属性被修改。

问题: - 当有相同方法的新调用时,完全停止Step1的最佳方法是什么。

2 个答案:

答案 0 :(得分:0)

您可以为异步方法使用取消令牌。 当调用GetRecords()方法时,执行GridData.Clear()。

这是一个非常简单的演示,但希望它有所帮助。

    public static List<int> List = new List<int>(); 

    public static async Task AddToList(CancellationToken cancellation)
    {
        List.Clear();

        for (var i = 0; i < 100 && !cancellation.IsCancellationRequested; i++)
        {
            await Task.Delay(100, cancellation);
            List.Add(i);
        }
    }

    public static async Task MainAsync(CancellationToken cancellation)
    {
        await AddToList(cancellation);
    }

    private static void Main(string[] args)
    {
        var cts = new CancellationTokenSource();
        Task.Run(() => MainAsync(cts.Token), cts.Token);

        Thread.Sleep(1000);
        cts.Cancel();

        cts = new CancellationTokenSource();
        Task.Run(() => MainAsync(cts.Token), cts.Token).Wait(cts.Token);

        Console.WriteLine(List.Count);
        Console.Read();
    }

希望这会有所帮助 在这里查看更多http://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx

答案 1 :(得分:0)

您可以这样做的一种方法是为Task存储RefreshRecords,然后在新请求到达时取消它。您需要修改GetRecords方法才能获得CancellationToken,并且您需要定期检查该令牌是否取消token.ThrowIfCancellationRequested(引发OperationCancelledException)或通过检查财产token.IsCancellationRequested。最好调用token.ThrowIfCancellationRequested,因为它正确地将任务转换为已取消状态。

您还需要同步对RefreshRecords方法的访问权限,并为每项任务创建新的CancellationTokenSource。您可以使用SemaphoreSlim异步等待上一个请求完成。

private Task refreshRecordsTask;
private CancellationTokenSource cts = new CancellationTokenSource();
private SemaphoreSlim sempahore = new SemaphoreSlim(1,1);

public async void RefreshRecords()
{
    try
    {
        await sempahore.WaitAsync();

        if(refreshRecordsTask != null && !refreshRecordsTask.IsCompleted)
        {
            cts.Cancel();
            cts = new CancellationTokenSource();            
        }

        refreshRecordsTask = GetRecords(cts.Token);
    }
    finally
    {
        sempahore.Release();
    }   

    await refreshRecordsTask;
}

private async Task GetRecords(CancellationToken token)
{
  int offset = 0;
  int limit = 50;

  do
  {
      token.ThrowIfCancellationRequested();
      var dummyData = await GetRangeRecord(offset, limit);
      GridData.Add(dummyData);

      offset += limit;

  } while (GridData.Count() < 1000);
}