c# - 任务<T>排队

标签 c# multithreading thread-safety async-await

我有以下方法

public async Task<T> SomeMethod(parameters)
{
    // here we execute some instructions which are not thread safe
}

我需要 SomeMethod 返回一个任务,以便其他方法可以异步运行(等待)它,而不是阻塞 UI 线程。

问题是 SomeMethod 可以并行调用,因为执行返回到 UI 线程,这会引发异常,因为 SomeMethod() 内的某些调用不是线程安全的。

确保对 SomeMethod 的所有调用都排队(并且可等待)并且该队列将按顺序执行的最佳方法是什么?

最佳答案

使用AsyncLock防止两个线程执行单个代码块:

(传统的 lock 不起作用,因为您不能在其中使用 await 关键字)

private AsyncLock myAsyncLock = new AsyncLock();

public async Task<T> SomeMethod(parameters)
{
    using (await myAsyncLock.LockAsync())
    {
       // here we execute some instructions which are not thread safe
    }
}


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, System.Threading.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();
        }
    }
}

// http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266983.aspx
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);
    }
}

关于c# - 任务<T>排队,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37880952/

相关文章:

c# - 复杂的测试 - 依赖于相同的测试数据

java - java多线程求和

java - Hashmap 的 containsKey 方法是线程安全的,如果映射被初始化一次,并且永远不会被再次修改

c# - 可以使用 ReactiveUI 根据两个列表执行

c# - 根据数据库中的另一个文本框填充文本框值

python - 每次迭代在多个 CPU 上训练不同的 scikit-learn 分类器

sql-server - 控制 azure WebJob 函数调用

java - 为什么 Java 的 SimpleDateFormat 不是线程安全的?

javascript - AngularJS/这种情况绝对安全吗?

c# - 无法在窗口上设置自定义位置