在Windows Phone 7中锁定异步方法

时间:2014-01-28 18:25:42

标签: c# windows-phone-7 locking windows-phone async-await

我使用自定义方法名称UploadDataToDropbboxAsync()将数据上传到按钮Command上的dropbox;我正在使用这种方法。

 RelayCommand _navigateToDropBoxUploadCommand;
    public ICommand NavigateToDropBoxUploadCommand
    {
        get
        {
            return _navigateToDropBoxUploadCommand = _navigateToDropBoxUploadCommand ?? new RelayCommand(
                    async () =>
                    {
                        await UploadDataToDropbox("sandbox");
                    });
        }
    }

所以当我在等待数据上传之前多次点击按钮时,thr会同时发生多个上传。 所以我想锁定这个方法,以便在第一次上传完成之前不会再次调用它。 任何形式的帮助表示赞赏。

2 个答案:

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