处理 async/await 时的锁定行为 [closed]

Locking behaviour when dealing with async/await [closed]

提问人:creativergk 提问时间:11/12/2023 最后编辑:creativergk 更新时间:11/12/2023 访问量:67

问:


想改进这个问题吗?通过编辑这篇文章添加详细信息并澄清问题。

9天前关闭。

我可能想多了,但我不确定在处理 await/async 时我是否理解锁定是如何工作的。

鉴于以下情况,在每次之后,不同的线程可以返回执行下一段代码,对吧?(除非在具有类似 WPF 的线程上运行)awaitTaskScheduler

await Task.Run(async () =>
{
    // ThreadPoolWorker 1

    await Something();

    // ThreadPoolWorker 2

    await SomethingElse();

    // ThreadPoolWorker 3

    await SomethingElse2();

    // ThreadPoolWorker 4
});

因此,考虑到这一点,我最终会遇到以下问题,我使用 .第一个任务进入 on ,锁也进入,但从另一个线程返回,例如 。Task.Run()WaitAsync()Thread1Thread1WaitAsyncThread 2

同时,在第二时间,进入了,但是当它从,理论上可以打开吗?那么现在就不用等锁了吗?Task.Run()Thread 5Task.Delay()awaitThread 1

Task.Run(async () =>
{
    // Thread 1
    await semaphoreSlim.WaitAsync();
    // Thread 2
    
    ...
});

Task.Run(async () =>
{
    // Thread 5
    await Task.Delay(5000);
    // Thread 1
    await semaphoreSlim.WaitAsync();

    ...
});

我猜处理它的方式是必须始终在调用它的线程上返回?这是在 SemaphoreSlim 本身实现的吗?await Semaphore.WaitAsync

C# async-await 锁定 任务

评论

0赞 Theodor Zoulias 11/12/2023
你不是也打电话到哪里吗?或者您刚刚删除了版本以保持示例简单?semaphoreSlim.Release()
0赞 creativergk 11/12/2023
是的,为了保持代码简短。
0赞 nbk 11/12/2023
信号量旨在停止所有进程以阻止关键部分,因此只有在 e 进程上才能传递所有其他等待直到超时命中。所以你的工作就是让所有的过程都及时处理,就像饥饿的哲学家一样
1赞 Theodor Zoulias 11/12/2023
我认为加回去会增加你的问题的清晰度。目前您正在谈论锁,但代码中没有任何内容传达互斥的意图。您还可以包含实例化 的代码,以便我们可以看到参数的值并获得一些见解。理想情况下,您应该发布一个最小且可重现的示例,其中包括实际和预期的行为。ReleasesemaphoreSliminitialCount
0赞 creativergk 11/12/2023
@TheodorZoulias我不是要展示一个可重复的例子。我问的是,是什么阻止了我提到的理论案例的发生。添加通常的 try/finally 来释放信号量不会为问题添加任何内容。

答:

0赞 DasKrümelmonster 11/12/2023 #1

使用具有输出和未满足的特定期望的完整代码示例,您的问题会更好。

但是,我会尝试以我理解的方式回答它。因此,对于此代码:

Task.Run(async () => // Task A
{
    // Thread 1
    await semaphoreSlim.WaitAsync();
    // Thread 2
});

Task.Run(async () => // Task B
{
    // Thread 5
    await Task.Delay(5000);
    // Thread 1
    await semaphoreSlim.WaitAsync();
});

你担心的是 2 个任务同时运行,线程 1 执行第一个任务的前半部分,因此具有信号量并可以绕过第二个任务等待?

那不会发生。请记住,任务是应按顺序执行的命令,可以由不同的线程执行,每个线程在每次等待后由另一个线程接管。但是,这也意味着等待实际上并未在任何线程上完成。可以这样想:

  • 线程 1 开始任务 A 的进度,并遇到等待。信号量块和任务被搁置,因为它此时无法进一步执行。
  • 同时,线程 5 开始了任务 B 的进度,并将其搁置一旁,因为它在接下来的 5 秒内无法执行
  • 线程池空闲
  • 主线程调用 Semaphore.Release()
  • 因此,任务 A 变为可执行任务,线程(线程 2)抓取它并进一步运行它。
  • 计时器过后,任务 B 再次符合条件。线程 1 抓住它并继续。调用 WaitAsync() 时,需要再次搁置 Task。这是因为信号量知道它的状态并且很忙。
  • 有人调用 Release()
  • 任务 B 再次符合条件,并且某些线程可以拾取它

或者换个说法,哪个线程启动 WaitAsync() 或哪个线程之后接管并不重要。重要的是,(完整的)信号量的发布将导致一个任务变得可执行,即使许多人正在等待它。

评论

0赞 creativergk 11/13/2023
谢谢。我困惑的根源还在于,我认为 SemaphoreSlim 具有线程亲和力,而实际上并非如此。所以 semaphoreSlim 不会有我想到的问题,......但是互斥锁会,因为它确实具有线程亲和力