我使用自定义方法名称UploadDataToDropbboxAsync()将数据上传到按钮Command上的dropbox;我正在使用这种方法。
RelayCommand _navigateToDropBoxUploadCommand;
public ICommand NavigateToDropBoxUploadCommand
{
get
{
return _navigateToDropBoxUploadCommand = _navigateToDropBoxUploadCommand ?? new RelayCommand(
async () =>
{
await UploadDataToDropbox("sandbox");
});
}
}
所以当我在等待数据上传之前多次点击按钮时,thr会同时发生多个上传。 所以我想锁定这个方法,以便在第一次上传完成之前不会再次调用它。 任何形式的帮助表示赞赏。
答案 0 :(得分:2)
我不会为此使用锁定。首先,你只处理一个线程 - 一个UI线程(至少在锁定方面 - 正如你真正要求的那样是锁定UI线程来运行这个命令)。另一方面,与重量相比,做一个更好的处理方法。
我只是在Async之前禁用该按钮,然后重新启用它。例如:
return _navigateToDropBoxUploadCommand = _navigateToDropBoxUploadCommand ?? new RelayCommand(
async () =>
{
myButton.IsEnabled = false;
await UploadDataToDropbox("sandbox");
myButton.IsEnabled = true;
});
但是,禁用按钮可能会更好地在其他地方完成,具体取决于您处理命令的方式等...(例如,如果您正在重复使用此命令进行多个UI点击/点击事件)。
答案 1 :(得分:0)
使用AsyncLock可以创建上传队列而不是同步上传:
private readonly AsyncLock _dropBoxUploadLock = new AsyncLock();
return _navigateToDropBoxUploadCommand = _navigateToDropBoxUploadCommand ?? new RelayCommand(
async () =>
{
var cachedStateFromUI = GetDataFromTheUI(...);
using(var releaser = await _dropBoxUploadLock.LockAsync())
{
await UploadDataToDropbox(cachedStateFromUI);
}
});
使用的AsyncLock类来自Stephen Toub http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx
的示例 /*
* private readonly AsyncLock m_lock = new AsyncLock();
* …
* using(var releaser = await m_lock.LockAsync())
* {
* … // protected code here
* }
*/
/// <summary>
/// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx
/// </summary>
public class AsyncLock
{
private readonly AsyncSemaphore m_semaphore;
private readonly Task<Releaser> m_releaser;
public AsyncLock()
{
m_semaphore = new AsyncSemaphore(1);
m_releaser = Task.FromResult(new Releaser(this));
}
public Task<Releaser> LockAsync()
{
var wait = m_semaphore.WaitAsync();
return wait.IsCompleted ?
m_releaser :
wait.ContinueWith((_, state) => new Releaser((AsyncLock)state),
this, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public struct Releaser : IDisposable
{
private readonly AsyncLock m_toRelease;
internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; }
public void Dispose()
{
if (m_toRelease != null)
m_toRelease.m_semaphore.Release();
}
}
}
AsyncLock依赖于AsyncSemaphore http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266983.aspx
/*
* private readonly AsyncSemaphore m_lock = new AsyncSemaphore(1);
* …
* await m_lock.WaitAsync();
* try
* {
* … // protected code here
* }
* finally { m_lock.Release(); }
*/
/// <summary>
/// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266983.aspx
/// </summary>
public class AsyncSemaphore
{
private readonly static Task s_completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool>> m_waiters = new Queue<TaskCompletionSource<bool>>();
private int m_currentCount;
public AsyncSemaphore(int initialCount)
{
if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");
m_currentCount = initialCount;
}
public Task WaitAsync()
{
lock (m_waiters)
{
if (m_currentCount > 0)
{
--m_currentCount;
return s_completed;
}
else
{
var waiter = new TaskCompletionSource<bool>();
m_waiters.Enqueue(waiter);
return waiter.Task;
}
}
}
public void Release()
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
toRelease.SetResult(true);
}
}