单线程异步异步式编程的信号量



信号量是一种多线程锁定机制,可确保仅在给定资源上运行有限数量的线程。静音是一个有限数字的特殊情况。

异步编程与多线程编程有很多共同点,即使它本来不是本质上的多线程。

以下代码创建了十个任务,只需等待一秒钟并记录其开始和结束。

所有这些都仅在一个线程上执行(我假设适当的同步上下文维护就适用于WPF中的情况(。

因此,即使我们只有一个线程我们有"并行"任务,并且可以在某些情况下只能将对资源的访问限制在其中的少数或一个任务中。(例如,限制并行网络请求。(

似乎需要一个"异步信号量" - 锁定线程而是异步连续性的概念。

我已经实施了这样的信号来检查它是否确实有意义并阐明了我的意思。

我的问题是:在.NET框架本身中,这件事已经可用吗?我找不到任何东西,尽管在我看来应该存在的。

所以这是代码(linqpad在此处共享(:

    async void Main()
    {
        // Necessary in LINQPad to ensure a single thread.
        // Other environments such as WPF do this for you.
        SynchronizationContext.SetSynchronizationContext(
            new DispatcherSynchronizationContext());
        var tasks = Enumerable.Range(1, 10).Select(SampleWork).ToArray();
        await Task.WhenAll(tasks);
        "All done.".Dump();
    }
    AsyncSemaphore commonSemaphore = new AsyncSemaphore(4);
    async Task SampleWork(Int32 i)
    {
        using (await commonSemaphore.Acquire())
        {
            $"Beginning work #{i} {Thread.CurrentThread.ManagedThreadId}".Dump();
            await Task.Delay(TimeSpan.FromSeconds(1));
            $"Finished work #{i} {Thread.CurrentThread.ManagedThreadId}".Dump();
        }
    }
    public class AsyncSemaphore
    {
        Int32 maxTasks;
        Int32 currentTasks;
        ReleasingDisposable release;
        Queue<TaskCompletionSource<Object>> continuations
            = new Queue<TaskCompletionSource<Object>>();
        public AsyncSemaphore(Int32 maxTasks = 1)
        {
            this.maxTasks = maxTasks;
            release = new ReleasingDisposable(this);
        }
        public async Task<IDisposable> Acquire()
        {
            ++currentTasks;
            if (currentTasks > maxTasks)
            {
                var tcs = new TaskCompletionSource<Object>();
                continuations.Enqueue(tcs);
                await tcs.Task;
            }
            return release;
        }
        void Release()
        {
            --currentTasks;
            if (continuations.Count > 0)
            {
                var tcs = continuations.Dequeue();
                tcs.SetResult(null);
            }
        }
        class ReleasingDisposable : IDisposable
        {
            AsyncSemaphore self;
            public ReleasingDisposable(AsyncSemaphore self) => this.self = self;
            public void Dispose() => self.Release();
        }
    }

我得到此输出:

Beginning work #1 1
Beginning work #2 1
Beginning work #3 1
Beginning work #4 1
Finished work #4 1
Finished work #3 1
Finished work #2 1
Finished work #1 1
Beginning work #5 1
Beginning work #6 1
Beginning work #7 1
Beginning work #8 1
Finished work #5 1
Beginning work #9 1
Finished work #8 1
Finished work #7 1
Finished work #6 1
Beginning work #10 1
Finished work #9 1
Finished work #10 1
All done.

所以,确实,我最多有4个任务正在运行,并且所有任务都在同一线程上运行。

因此,即使我们只有一个线程我们有"并行"任务

我通常更喜欢术语"并发",只是为了避免与Parallel/Parallel Linq。

混淆。

我的问题是:这件事已经可用,理想情况下在.NET框架本身中?

是。SemaphoreSlim是一个可以同步使用的信号量异步。

我在Nuget上也有一套完整的异步协调基础,灵感来自Stephen Toub的博客文章有关该主题的博客文章。我的原始词都是同步和迅速兼容(和threadSafe(,如果资源的一个用户是同步的,但其他用户是异步的。

是同步的。

最新更新