6

我有一个名为 Refresh 的异步操作。如果在第一次完成之前进行第二次刷新调用,我需要对其进行排队。这就是我所拥有的:

public async Task Refresh(RefreshArgs refreshArgs)
{
    await EnqueueRefreshTask(refreshArgs);
}

private Queue<RefreshArgs> refreshQueue =
    new Queue<RefreshArgs>();

private async Task EnqueueRefreshTask(RefreshArgs refreshArgs)
{
    refreshQueue.Enqueue(refreshArgs);

    await ProcessRefreshQueue();
}

private Task currentRefreshTask = null;

private async Task ProcessRefreshQueue()
{
    if ((currentRefreshTask == null) || (currentRefreshTask.IsCompleted))
    {
        if (refreshQueue.Count > 0)
        {
            var refreshArgs = refreshQueue.Dequeue();

            currentRefreshTask = DoRefresh(refreshArgs);
            await currentRefreshTask;

            await ProcessRefreshQueue();
        }           
    }
}

private async Task DoRefresh(RefreshArgs refreshArgs)
{
    // Lots of code here, including calls to a server that are executed with await.
    // Code outside my control may make another Refresh(args) call while this one is still processing.
    // I need this one to finish before processing the next.
}

它有效,但我不确定这是使用任务执行此操作的最佳方式。有什么想法吗?

更新:

我尝试使用 ActionBlock:

public async Task Refresh(RefreshArgs refreshArgs)
{
    if (refreshActionBlock == null)
    {
        var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions();
        executionDataflowBlockOptions.MaxMessagesPerTask = 1;
        executionDataflowBlockOptions.TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();

        refreshActionBlock = new ActionBlock<RefreshArgs>(args => DoRefresh(args), executionDataflowBlockOptions);
    }

    await refreshActionBlock.SendAsync(refreshArgs);
}

这将 DoRefresh 排队,并允许它在 UI 线程中运行(我需要)。问题是 SendAsync 不等待 DoRefresh 的工作。

SendAsync:“异步向目标消息块提供消息,允许延迟”。我只在等待发送,而不是操作本身。

这样做不会按预期工作:

await Refresh(RefreshArgs.Something);
// other code goes here. It expects the first refresh to be finished.
await Refresh(RefreshArgs.SomethingElse);
// other code goes here. It expects the second refresh to be finished.

ActionBlock 将对第二次刷新进行排队,但在刷新完成之前等待就结束了。我需要他们在 DoRefresh 的工作完成后返回。

4

3 回答 3

3

我认为最简单的方法是使用AsyncLock. 您可以从 Stephen Cleary 的库 AsyncEx中获得一个,或者您可以阅读Stephen Toub 关于如何自己构建它的文章

如果你有AsyncLock,实现你的Refresh()很简单:

public async Task Refresh(RefreshArgs refreshArgs)
{
    using (await m_lock.LockAsync())
    {
        // do your async work here
    }
}

这将确保Refresh()es 一个接一个地执行(而不是交错),并且Task返回 fromRefresh()仅在Refresh()实际完成后完成。

可以使用ActionBlockfrom TPL Dataflow 来做同样的事情,但您还需要使用TaskCompletionSource它,它会比AsyncLock版本复杂得多。

于 2013-04-10T19:25:11.753 回答
2

你可以看看这些帖子,他们谈论异步协调(信号量、重置事件)和使用任务的排除:

[编辑:添加第 6 部分]

于 2013-04-10T15:33:55.143 回答
0

这很简单 :) 只有你需要使用“switch-case”!现在让我们向您展示如何使用小示例代码。示例场景:我们要下载 2 张图片,一张一张作为队列。

命名空间测试{

public partial class SampleOfQueue : Form
{

    public SampleOfQueue() { InitializeComponent(); }
    DBContext db = new DBContext();
    int Queue;

    private void btnStartApp_Click(object sender, EventArgs e)
    {
        Queue = 0;
        DownloadQueue();
    }

    private void DownloadQueue()
    {
        switch (Queue)
        {
            case 0:
                {
                    DownloadFile("http://images.forbes.com/media/lists/53/2009/tom-cruise.jpg", "Tom Cruise 1", "");
                    Queue += 1; break;
                }
            case 1:
                {
                    DownloadFile("https://upload.wikimedia.org/wikipedia/commons/6/69/Tom_Cruise_Collateral.jpg", "Tom Cruise 2", "");
                    Queue += 1; break;
                }
            case 2:
                {
                    // Other....
                    Queue += 1; break;
                }
            default: break;
        }

    }

    public void DownloadFile(string urlAddress, string ImageName, string ImageFarsiName)
    {
        WebClient webClient = new WebClient();
        webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);

        Uri URL = urlAddress.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ? new Uri(urlAddress) : new Uri(urlAddress);
        webClient.DownloadFileAsync(URL, "d:\\x.jpg");

    }

    private void Completed(object sender, AsyncCompletedEventArgs e)
    {
        DownloadQueue();
        if (Queue == 3) { MessageBox.Show("finish"); }
    }

}

}

在 VS 2015 windows 窗体中使用 C# 4.6 测试。我自己的创新方法:) 我希望有用 <3 祝你好运。

于 2015-12-25T09:56:35.400 回答