【问题标题】:Non blocking locking非阻塞锁定
【发布时间】:2012-02-09 10:58:12
【问题描述】:

我想为一个重复操作启动一些新线程。但是当这样的操作已经在进行时,我想丢弃当前的任务。在我的场景中,我只需要非常当前的数据 - 丢弃的数据不是问题。

在 MSDN 中,我找到了 Mutex 类,但据我了解,它等待轮到它,阻塞当前线程。另外我想问你:.NET 框架中是否已经存在某些东西,它执行以下操作

  1. 某个方法 M 是否已经在执行?
  2. 如果是这样,return(让我增加一些统计计数器)
  3. 如果没有,则在新线程中启动方法 M

【问题讨论】:

    标签: c# .net multithreading


    【解决方案1】:

    您可能遇到过的lock(someObject) 语句是Monitor.EnterMonitor.Exit 周围的语法糖。

    但是,如果您以这种更详细的方式使用监视器,您还可以使用Monitor.TryEnter,它允许您检查您是否能够获得锁定 - 从而检查其他人是否已经拥有它并正在执行代码。

    所以不要这样:

    var lockObject = new object(); 
    
    lock(lockObject)
    {
        // do some stuff
    }
    

    试试这个(选项 1)

    int _alreadyBeingExecutedCounter;
    var lockObject = new object();
    
    if (Monitor.TryEnter(lockObject))
    {
       // you'll only end up here if you got the lock when you tried to get it - otherwise you'll never execute this code.
    
        // do some stuff
    
        //call exit to release the lock
        Monitor.Exit(lockObject);
    }
    else
    {
        // didn't get the lock - someone else was executing the code above - so I don't need to do any work!
       Interlocked.Increment(ref _alreadyBeingExecutedCounter);
    }
    

    (你可能想尝试一下..finally 以确保释放锁)

    或完全放弃显式锁定并执行此操作

    (选项 2)

    private int _inUseCount;
    
    public void MyMethod()
    {
        if (Interlocked.Increment(ref _inUseCount) == 1)
        {
            // do dome stuff    
        }
        Interlocked.Decrement(ref _inUseCount);
    }
    

    [编辑:回答您关于this的问题]

    不 - 不要使用 thislock。创建一个私有范围的对象作为您的锁。

    否则你有这个潜在的问题:

    public class MyClassWithLockInside
    {
        public void MethodThatTakesLock()
        {
            lock(this)
            {
                // do some work
            }
        }
     }
    
    public class Consumer
    {
        private static MyClassWithLockInside _instance = new MyClassWithLockInside();
    
        public void ThreadACallsThis()
        {
              lock(_instance)
              {
                  // Having taken a lock on our instance of MyClassWithLockInside,
                  // do something long running
                  Thread.Sleep(6000);
               }
        }
    
        public void ThreadBCallsThis()
        {
             // If thread B calls this while thread A is still inside the lock above,
             // this method will block as it tries to get a lock on the same object
             // ["this" inside the class = _instance outside]
             _instance.MethodThatTakesLock();
        }  
    }
    

    在上面的例子中,一些外部代码已经设法破坏了我们类的内部锁定,只是通过对外部可访问的东西进行锁定。

    最好创建一个您可以控制的私有对象,并且您的班级之外的任何人都无法访问,以避免此类问题;这包括不使用this 或类型本身typeof(MyClassWithLockInside) 进行锁定。

    【讨论】:

    • this 会是一个合适的 lockObject 吗?
    【解决方案2】:

    一种选择是使用可重入哨兵:

    您可以定义一个int 字段(以0 初始化)并在进入方法时通过Interlocked.Increment 对其进行更新,并且仅在它为1 时继续。最后只需执行Interlocked.Decrement

    另一种选择:

    从您的描述看来,您有一个生产者-消费者-场景...

    对于这种情况,使用 BlockingCollection 之类的东西可能会有所帮助,因为它是线程安全的,而且大部分情况下是无锁的......

    另一种选择是使用ConcurrentQueueConcurrentStack...

    【讨论】:

    • @DerMike 我会给你一个例子,但我需要更多信息......基本上这些集合有Try不会阻止的方法......请查看我上面关于@987654333的更新@用法...
    【解决方案3】:

    您可能会在以下site 上找到一些有用的信息(该 PDf 也可下载 - 最近自己下载了)。 Adavnced threading Suspend and Resume 或 Aborting 章节可能是您最感兴趣的。

    【讨论】:

    【解决方案4】:

    您应该使用 Interlocked 类原子操作 - 以获得最佳性能 - 因为您实际上不会使用系统级同步(任何“标准”原语都需要它,并且涉及系统调用开销)。 //简单的无所有权的不可重入互斥体,易于改造以支持 //这些功能(只需在获取锁后设置所有者(例如将线程引用与 Thread.CurrentThread 进行比较),并检查匹配身份,添加计数器以进行重入) // 不能使用 bool,因为 CompareExchange 不支持它 私有 int 锁;

    public bool TryLock()
    {
      //if (Interlocked.Increment(ref _inUseCount) == 1)      
      //that kind of code is buggy - since counter can change between increment return and
      //condition check - increment is atomic, this if - isn't.       
      //Use CompareExchange instead
      //checks if 0 then changes to 1 atomically, returns original value
      //return true if thread succesfully occupied lock
      return CompareExchange(ref lock, 1, 0)==0;
      return false;
    
    }
    public bool Release()
    {
      //returns true if lock was occupied; false if it was free already
      return CompareExchange(ref lock, 0, 1)==1;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-03-13
      • 1970-01-01
      • 2021-07-25
      • 2014-04-04
      • 1970-01-01
      • 2016-07-06
      • 1970-01-01
      • 2013-01-16
      相关资源
      最近更新 更多