C# 对异步方法的队列调用
我有一个名为Refresh的异步操作。如果在第一次调用完成之前进行了第二次刷新调用,我需要对其进行排队。这就是我所拥有的:C# 对异步方法的队列调用,c#,queue,async-await,C#,Queue,Async Await,我有一个名为Refresh的异步操作。如果在第一次调用完成之前进行了第二次刷新调用,我需要对其进行排队。这就是我所拥有的: public async Task Refresh(RefreshArgs refreshArgs) { await EnqueueRefreshTask(refreshArgs); } private Queue<RefreshArgs> refreshQueue = new Queue<RefreshArgs>(); priv
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将对第二次刷新进行排队,但在刷新完成之前,等待将结束。我需要他们在DoRefresh完成工作后返回。你可以看看这些帖子,它们讨论异步协调(信号量、重置事件)和使用任务的排除:
- 我认为最简单的方法是使用
异步锁。你可以买一本,也可以阅读
使用AsyncLock
时,实现Refresh()
非常简单:
public async Task Refresh(RefreshArgs refreshArgs)
{
using (await m_lock.LockAsync())
{
// do your async work here
}
}
这将确保Refresh()
您可以使用TPL数据流中的ActionBlock
来执行同样的操作,但是您还需要使用TaskCompletionSource
,它比AsyncLock
版本复杂得多。这非常简单:)
只有你需要使用“开关盒”!!
现在,让我们来演示如何使用小样本代码。
示例场景:我们想下载2个pic,一个接一个作为队列
名称空间测试
{
}
在VS 2015 windows窗体中使用C#4.6进行测试。
我自己的创新方法:)
我希望你能用一个
ActionBlock
来代替这一切,这是我想到的。在处理异步数据序列时,TPL数据流(ActionBlock是其中的一部分)是一种很好的方法。我认为这更多地属于codereview站点。请注意,Queue
不是线程安全的;您应该改为使用BlockingCollection
。还有ConcurrentQueue-您在最有用的原语之前停止:AsyncLock
。Stephen Toub在一篇文章中谈到了这一点。
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.
public async Task Refresh(RefreshArgs refreshArgs)
{
using (await m_lock.LockAsync())
{
// do your async work here
}
}
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"); }
}
}