Locking with timeout pattern

那年仲夏 提交于 2019-11-30 04:50:56

问题


lock uses this pattern

if(Monitor.Enter(lock))
    try
    {
        ...
    }
    finally { Monitor.Exit(lock); } // using this style to reduce post "height"

if we don't want to wait infinite we can provide timeout

if(!Monitor.TryEnter(lock, timeout))
    throw new TimeoutException();
try
{
    ...
}
finally { Monitor.Exit(lock); }

I have scenario when method has to obtain multiple locks before it start doing anything. This looks awful:

if(!Monitor.TryEnter(lockA, timeout))
    throw new TimeoutException();
try
{
    if(!Monitor.TryEnter(lockB, timeout))
        throw new TimeoutException();
    try
    {
        if(!Monitor.TryEnter(lockC, timeout))
            throw new TimeoutException();
        try
        {
            ... // more of such constructions
        }
        finally { Monitor.Exit(lockC); }
    }
    finally { Monitor.Exit(lockB); }
}
finally { Monitor.Exit(lockA); }

It has problems:

  • looks ugly (the method code is indented, imagine how it will looks for lockZ), can be solved by putting method code into another method.

  • locking occurs synchronously, so the worst successful case may take time slightly less than a sum of all timeouts.

Is there a way to improve this timeout pattern?

I was thinking to make a method with delegate parameter and lock to achieve something like linq chaining (but to also run locks in parallel, this is a challenge):

Lock(lockA).Lock(lockB).Lock(lockC).Run( () => ...);

Or perhaps there is another way?


回答1:


I would go with usings for sync lock:

using System;
using System.Threading; 

public class MyLock : IDisposable
{
    private object lockObj;

    public MyLock(object lockObj, TimeSpan timeout)
    {
        this.lockObj = lockObj;
        if (!Monitor.TryEnter(this.lockObj, timeout))
            throw new TimeoutException();
    }

    public void Dispose()
    {
        Monitor.Exit(lockObj);
    }
}

Usage:

using(new MyLock(lockA, new TimeSpan.FromSeconds(1)))
using(new MyLock(lockB, new TimeSpan.FromSeconds(2)))
using(new MyLock(lockC, new TimeSpan.FromSeconds(3)))
{
    // your code
}

Do not know if "locking" in ctor is good pattern / design, but it will work ;)

For async. parallelization is not good idea. Why? If some thread will enter the monitor, the same thread must leave it (exit with lock). So if you lock on objA within Parallel.ForEach (f.e.) you will not know which thread has done it. So you won't be able to release it.



来源:https://stackoverflow.com/questions/37961521/locking-with-timeout-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!