简体   繁体   English

C#lambdas中的线程安全

[英]Thread safety in C# lambdas

I came across a piece of C# code like this today: 我今天遇到了一段这样的C#代码:

lock(obj)
{
  //  perform various operations
  ...

  //  send a message via a queue but in the same process, Post(yourData, callback)
  messagingBus.Post(data, () => 
  {
    //  perform operation
    ...
    if(condition == true)
    {
      //  perform a long running, out of process operation
      operation.Perform();
    }
  }
}

My question is this: can the callback function ever be invoked in such a way as to cause the lock(obj) to not be released before operation.Perform() is called? 我的问题是这样的:回调函数是否曾经以一种导致在调用operation.Perform()之前释放lock(obj)的方式被调用? ie, is there a way that the callback function can be invoked on the same thread that is holding the lock, and before that thread has released the lock? 即,有没有一种方法可以在持有该锁的同一线程上并且在该线程释放该锁之前调用该回调函数?

EDIT: messagingBus.Post(...) can be assumed to be an insert on to a queue, that then returns immediately. 编辑:messagingBus.Post(...)可以假定为插入到队列中,然后立即返回。 The callback is invoked on some other thread, probably from the thread pool. 该回调是在其他一些线程上调用的,可能是从线程池中调用的。

For the operation.Perform() you can read it as Thread.Sleep(10000) - just something that runs for a long time and doesn't share or mutate any state. 对于operation.Perform(),您可以将其读取为Thread.Sleep(10000)-只是可以长时间运行并且不会共享或改变任何状态的东西。

I'm going to guess. 我猜。

Post in .net generally implies that the work will be done by another thread or at another time. .net中的Post通常意味着工作将由另一个线程或在另一个时间完成。

So yes, it's not only possible that the lock on obj will be released before Perform is called, it's fairly likely it will happen. 因此,是的,不仅可能在调用Perform之前释放对obj的锁定,而且很有可能会发生。 However, it's not guaranteed. 但是,不能保证。 Perform may complete before the lock is released. Perform可能会在释放锁定之前完成。

That doesn't mean it's a problem. 这并不意味着有问题。 The "perform various actions" part may need the lock. “执行各种操作”部分可能需要锁定。 messagingBus may need the lock to queue the action. essagingBus可能需要锁才能将操作排队。 The work inside may not need the lock at all, in which case the code is thread safe. 内部的工作可能根本不需要锁,在这种情况下,代码是线程安全的。

This is all a guess because there's no notion of what work is being done, why it must be inside a lock, and what Post or perform does. 这全是猜测,因为没有概念来说明正在执行什么工作,为什么必须将其放在锁中以及Post或执行的工作。 So the code may be perfectly safe, or it may be horribly flawed. 因此,该代码可能是完全安全的,或者可能存在严重的缺陷。

Without know what messagingBus.Post is doing, you can't tell. 不知道messagementBus.Post在做什么,就无法分辨。 If Post invokes the delegate it is given (the lambda expression in your example) then the lock will be in place while that lambda executes. 如果 Post调用了委托(在您的示例中为lambda表达式),则在执行该lambda时将锁定该锁。 If Post schedules that delegate for execution at a later time, then the lock will not be in place while the lambda executes. 如果 Post安排该委托在以后执行,则在执行lambda时,锁不会处于适当位置。 It's not clear what the the lock(obj) is for, to lock calls to messagingBus.Post, or what... Detailing the type (including full namespace) of the messagingBus variable would go a long way to providing better details. 目前尚不清楚lock(obj)的用途是什么,用于锁定对messages.Bus.Post的调用,还是什么...详细说明messages.Bus变量的类型(包括完整的名称空间)将大大有助于提供更好的细节。

If the callback executes asynchronously, then yes, the lock may still be held when Perform() unless Post() does something specific to avoid that case (which would be unusual). 如果回调是异步执行的,则可以,当Perform()时,该锁可能仍会保持,除非Post()做一些特定的操作来避免这种情况(这是不寻常的)。

If the callback was scheduled on the same thread as the call to Post() (eg in the extreme example where the thread pool has only 1 thread), a typical thread pool implementation would not execute the callback until the thread finishes it's current task, which in this case would require it releasing the lock before executing Perform(). 如果回调与调用Post()安排在同一线程上(例如,在极端示例中,线程池只有1个线程),那么典型的线程池实现将在线程完成其当前任务之前不执行回调,在这种情况下,这将要求它在执行Perform()之前释放锁。

It's impossible to answer your question without knowing how messagingBus.Post is implemented. 如果不知道messagingBus.Post是如何实现的,就不可能回答您的问题。 Async APIs typically provide no guarantee that the callback will be executed truly concurrently. 异步API通常不保证回调将真正并发执行。 For example, .Net APM methods such as FileStream.BeginRead may decide to perform the operation synchronously, in wich case the callback will be executed on the same thread that called BeginRead. 例如,.Net APM方法(例如FileStream.BeginRead)可能决定同步执行该操作,在这种情况下,回调将在称为BeginRead的同一线程上执行。 Returned IAsyncResult.CompletedSynchronously will be set to true in this case. 在这种情况下,返回的IAsyncResult.CompletedSynchronously将设置为true。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM