【问题标题】:How to properly lock a Task.Run() block如何正确锁定 Task.Run() 块
【发布时间】:2019-10-22 18:40:04
【问题描述】:

我正在编写一个应用程序,其中我使用了几种访问某些共享资源的方法,因此通过lock(thisLock){ [...] ] 实现了一些安全性

一切都很好,直到我不得不在异步任务中使用资源。 这是代码:

private object thisLock = new object();
[...]
private void UpdateStuff()
{
lock(thisLock)
   {
     Task.Run(()=>{[code where I use shared resources]});
   }
}

它似乎工作正常,但我想知道这是否是正确的做法,或者我是否应该将 lock() 部分放在 Run() 中,如下所示:

private void UpdateStuff()
{

     Task.Run( () => {
        lock(thisLock)
        {
         [code where I use shared resources]
        }
     });    
}

我试图获取一些信息,但我发现的所有内容都提到了使用关键字 async 时的情况,而事实似乎并非如此。

所以,我的问题是:哪一种是最好/正确的方式来使用带有 Task.Run() 的 lock()?为什么?

谢谢!

编辑:为了澄清,我的疑问是锁定何时以及多少有效,所以基本上我问的是在第一种情况下是否有可能锁定仅对 Run() 调用有效,因为调用本身的内容可能存在于另一个线程中,而在第二种情况下,锁将与内容相关联,因此是有效的。

【问题讨论】:

  • 可以避免锁定吗?例如。 Task 可能会返回一个结果,而不是具有可变数据,而该结果可以继续使用,那么您不需要锁定可变数据。
  • 不是,因为我想保留任务代码中使用的资源,而不是任务本身的输出。

标签: c# locking task


【解决方案1】:

您的第一种方法是完全错误的。 Task.Run 可以在同一个线程上同步执行工作或将其排队到线程池中。你不知道会发生什么。

您无需等待任务完成,因此如果它排队到线程池中,您的函数将释放锁并返回,可能在工作开始之前。

一般来说,最好的做法是只在需要的时间内获取锁,并且永远不要在等待另一个线程时保持锁,因为如果你不特别小心,很容易导致死锁。

【讨论】:

  • 所以,如果我理解正确,第二种方法是最正确的方法,因为在第一种方法中,锁仅对Run() 调用有效,但对整个{ [code where I use shared resources] } 无效部分,对吧?
  • 是的,它有时可能只获取Run() 的锁,而不是任务中的代码。
【解决方案2】:

这两者之间没有最好的方法,你只会得到两种不同的结果:

在第一种情况下,在锁打开之前您不会开始任务。

在第二种情况下,您将启动一个在锁打开之前不会执行任何操作的任务。

在我看来,在需要之前不要打开任务,这是一种更好的方法,但这只是个人意见。开启和锁定之后,会分配无用的资源。

【讨论】:

  • 我的担忧来自于此:如果我理解正确,在第一种情况下,锁只会保留运行调用,并且由于它是异步的,因此不会立即打开锁,从而使其效果无效?相反,在第二种情况下,锁包含在任务中,所以一切都应该正确保留,对吧?
  • 如果类是同步或异步的,这段代码将以完全相同的方式工作
  • 也许我没明白你的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-16
  • 2023-01-17
  • 1970-01-01
  • 2011-07-31
  • 1970-01-01
  • 2021-02-04
相关资源
最近更新 更多