什么是不对的解决方案,以锁定和管理锁定例外? [英] What is wrong with this solution to locking and managing locked exceptions?

查看:121
本文介绍了什么是不对的解决方案,以锁定和管理锁定例外?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是在我的应用程序的线程安全功能和异常处理的约定。我是比较新的线程管理/多线程的概念。我使用 .NET 3.5

我写了下面的辅助方法来包装我的所有锁定动作看完这篇文章<一后href="http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx">http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx,这是在回答这个问题的联系,显示器VS锁

我的想法是,如果我在我的应用程序不断使用这种约定,这将是更容易编写线程安全的code和处理内部线程安全的code错误,而不会破坏状态。

 公共静态类锁定
{

    私人静态只读字典&LT;对象,布尔&GT; CorruptionStateDictionary =新字典&LT;对象,布尔&GT;();
    私人静态只读对象CorruptionLock =新的对象();

    公共静态布尔TryLockedAction(对象lockObject,行动行动,出异常除外)
    {
        如果(IsCorrupt(lockObject))
        {
            异常=新LockingException(不能对损坏的对象执行锁定的动作。);
            返回false;
        }
        异常= NULL;
        Monitor.Enter(lockObject);
        尝试
        {
            action.Invoke();
        }
        赶上(例外前)
        {
            异常=前;
        }
        最后
        {
            锁定(CorruptionLock)//我不想放开lockObject直到腐败的状态更新。
                                    //只要调用类锁定通过TryLockedAction()的lockObject,这应该工作
            {
                Monitor.Exit(lockObject);
                如果(例外!= NULL)
                {
                    如果(CorruptionStateDictionary.ContainsKey(lockObject))
                    {
                        CorruptionStateDictionary [lockObject] = TRUE;
                    }
                    其他
                    {
                        CorruptionStateDictionary.Add(lockObject,真正的);
                    }
                }
            }
        }
        回报例外== NULL;
    }

    公共静态无效廉洁的(对象corruptLockObject)
    {
        如果(IsCorrupt(corruptLockObject))
        {
            锁定(CorruptionLock)
            {
                CorruptionStateDictionary [corruptLockObject] = FALSE;
            }
        }
        其他
        {
            如果(!CorruptionStateDictionary.ContainsKey(corruptLockObject))
            {
                抛出新LockingException(廉洁()是不是还没有被损坏的对象有效。);
            }
            其他
            {
                //对象有previously是两袖清风。
                //我的想法是忽略呼叫。
            }
        }
    }

    公共静态布尔IsCorrupt(对象lockObject)
    {
        锁定(CorruptionLock)
        {
            返回CorruptionStateDictionary.ContainsKey(lockObject)及&安培; CorruptionStateDictionary [lockObject]
        }
    }


}
 

我用LockingException类,便于调试。

 公共类LockingException:异常
    {
        公共LockingException(字符串消息):基地(消息){}
    }
 

下面是一个例子使用级,以显示我打算如何使用这一点。

 公共类ExampleUsage
{
    私人只读对象ExampleLock =新的对象();

    公共无效ExecuteLockedMethod()
    {
        例外的例外;
        布尔有效= Locking.TryLockedAction(ExampleLock,ExecuteMethod,出除外);
        如果(!有效)
        {
            布尔重新验证= EnsureValidState();
            如果(重新验证)
            {
                Locking.Uncorrupt(ExampleLock);
            }
        }
    }

    私人无效ExecuteMethod()
    {
        //做一些事情,也许会抛出异常

    }

    公共BOOL EnsureValidState()
    {
        // code,以确保国家有效
        //如果有异常返回false,

        返回true;
    }
}
 

解决方案

您的解决方案似乎不用多说,但复杂性,由于在TryLockedAction比赛:


        如果(IsCorrupt(lockObject))
        {
            异常=新LockingException(不能对损坏的对象执行锁定的动作。);
            返回false;
        }
        异常= NULL;
        Monitor.Enter(lockObject);

在lockObject可能会变为破坏,而我们仍然在等待的Monitor.Enter,因此没有任何保护措施。

我不知道你会什么样的行为要实现,但也许这将有助于独立的锁定和状态管理:


类StateManager的
{
    公共BOOL IsCorrupted
    {
        得到;
        组;
    }

    公共无效执行(动作的身体,Func键fixState)
    {
        如果(this.IsCorrupted)
        {
            //这里使用了一些异常的派生类。
            抛出新的异常(无法在一个已损坏的对象执行操作。);
        }

        尝试
        {
            体();
        }
        赶上(例外)
        {
            this.IsCorrupted = TRUE;
            如果(fixState())
            {
                this.IsCorrupted = FALSE;
            }

            扔;
        }
    }
}

公共类ExampleUsage
{
    私人只读对象ExampleLock =新的对象();
    私人只读StateManager的StateManager的=新StateManager的();

    公共无效ExecuteLockedMethod()
    {
        锁定(ExampleLock)
        {
            stateManager.Execute(ExecuteMethod,EnsureValidState);
        }
    }

    私人无效ExecuteMethod()
    {
        //做一些事情,也许会抛出异常

    }

    公共BOOL EnsureValidState()
    {
        // code,以确保国家有效
        //如果有异常返回false,

        返回true;
    }
}

此外,据我了解,这篇文章的观点是,国家管理是并发presence更难。但是,它仍然只是你的对象状态的正确性问题,它是正交的锁定,可能需要使用完全不同的方法来确保正确性。例如。而不是改变一些复杂的状态withing锁定code区,创建一个新的,如果它成功了,只是切换到新的状态,在一个单一的,简单的引用赋值:


公共类ExampleUsage
{
    私人ExampleUsageState状态=新ExampleUsageState();

    公共无效ExecuteLockedMethod()
    {
        变种newState = this.state.ExecuteMethod();
        this.state = newState;
    }
}

公共类ExampleUsageState
{
    公共ExampleUsageState ExecuteMethod()
    {
        //做一些事情,也许会抛出异常
    }
}

我个人总是倾向于认为手动锁定被硬够对待,当你分别需要的每一种情况下(所以在一般的状态管理解决方案,没有太大的必要)和低lelvel-足够的工具来使用它真的有节制地。

My objective is a convention for thread-safe functionality and exception handling within my application. I'm relatively new to the concept of thread management/multithreading. I am using .NET 3.5

I wrote the following helper method to wrap all my locked actions after reading this article http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx, which was linked in response to this question, Monitor vs lock.

My thought is that if I use this convention consistently in my application, it will be easier to write thread-safe code and to handle errors within thread safe code without corrupting the state.

public static class Locking
{

    private static readonly Dictionary<object,bool> CorruptionStateDictionary = new Dictionary<object, bool>(); 
    private static readonly object CorruptionLock = new object();

    public static bool TryLockedAction(object lockObject, Action action, out Exception exception)
    {
        if (IsCorrupt(lockObject))
        {
            exception = new LockingException("Cannot execute locked action on a corrupt object.");
            return false;
        }
        exception = null;
        Monitor.Enter(lockObject);
        try
        {
            action.Invoke();
        }
        catch (Exception ex)
        {
            exception = ex;
        }
        finally
        {
            lock (CorruptionLock)   // I don't want to release the lockObject until its corruption-state is updated.
                                    // As long as the calling class locks the lockObject via TryLockedAction(), this should work
            {
                Monitor.Exit(lockObject);
                if (exception != null)
                {   
                    if (CorruptionStateDictionary.ContainsKey(lockObject))
                    {
                        CorruptionStateDictionary[lockObject] = true;
                    }
                    else
                    {
                        CorruptionStateDictionary.Add(lockObject, true);
                    }
                }
            }
        }
        return exception == null;
    }

    public static void Uncorrupt(object corruptLockObject)
    {
        if (IsCorrupt(corruptLockObject))
        {
            lock (CorruptionLock)
            {
                CorruptionStateDictionary[corruptLockObject] = false;
            }
        }
        else
        {
            if(!CorruptionStateDictionary.ContainsKey(corruptLockObject))
            {
                throw new LockingException("Uncorrupt() is not valid on object that have not been corrupted."); 
            }
            else
            {
                //  The object has previously been uncorrupted.
                //  My thought is to ignore the call.
            }
        }
    }

    public static bool IsCorrupt(object lockObject)
    {
        lock(CorruptionLock)
        {
            return CorruptionStateDictionary.ContainsKey(lockObject) && CorruptionStateDictionary[lockObject];
        }
    }


}

I use a LockingException class for ease of debugging.

    public class LockingException : Exception
    {
        public LockingException(string message) : base(message) { }
    }

Here is an example usage class to show how I intend to use this.

public class ExampleUsage
{
    private readonly object ExampleLock = new object();

    public void ExecuteLockedMethod()
    {
        Exception exception;
        bool valid = Locking.TryLockedAction(ExampleLock, ExecuteMethod, out exception);
        if (!valid)
        {
            bool revalidated = EnsureValidState();
            if (revalidated)
            {
                Locking.Uncorrupt(ExampleLock);
            }
        }
    }

    private void ExecuteMethod()
    {
        //does something, maybe throws an exception

    }

    public bool EnsureValidState()
    {
        // code to make sure the state is valid
        // if there is an exception returns false,

        return true;
    }
}

解决方案

Your solution seems to add nothing but complexity due to a race in the TryLockedAction:


        if (IsCorrupt(lockObject))
        {
            exception = new LockingException("Cannot execute locked action on a corrupt object.");
            return false;
        }
        exception = null;
        Monitor.Enter(lockObject);

The lockObject might become "corrupted" while we are still waiting on the Monitor.Enter, so there is no protection.

I'm not sure what behaviour you'd like to achieve, but probably it would help to separate locking and state managing:


class StateManager
{
    public bool IsCorrupted
    {
        get;
        set;
    }

    public void Execute(Action body, Func fixState)
    {
        if (this.IsCorrupted)
        {
            // use some Exception-derived class here.
            throw new Exception("Cannot execute action on a corrupted object.");
        }

        try
        {
            body();
        }
        catch (Exception)
        {
            this.IsCorrupted = true;
            if (fixState())
            {
                this.IsCorrupted = false;
            }

            throw;
        }
    }
}

public class ExampleUsage
{
    private readonly object ExampleLock = new object();
    private readonly StateManager stateManager = new StateManager();

    public void ExecuteLockedMethod()
    {
        lock (ExampleLock)
        {
            stateManager.Execute(ExecuteMethod, EnsureValidState);
        }
    }

    private void ExecuteMethod()
    {
        //does something, maybe throws an exception

    }

    public bool EnsureValidState()
    {
        // code to make sure the state is valid
        // if there is an exception returns false,

        return true;
    }
}

Also, as far as I understand, the point of the article is that state management is harder in presence of concurrency. However, it's still just your object state correctness issue which is orthogonal to the locking and probably you need to use completely different approach to ensuring correctness. E.g. instead of changing some complex state withing locked code region, create a new one and if it succeeded, just switch to the new state in a single and simple reference assignment:


public class ExampleUsage
{
    private ExampleUsageState state = new ExampleUsageState();

    public void ExecuteLockedMethod()
    {
        var newState = this.state.ExecuteMethod();
        this.state = newState;
    }
}

public class ExampleUsageState
{
    public ExampleUsageState ExecuteMethod()
    {
        //does something, maybe throws an exception
    }
}

Personally, I always tend to think that manual locking is hard-enough to treat each case when you need it individually (so there is no much need in generic state-management solutions) and low-lelvel-enough tool to use it really sparingly.

这篇关于什么是不对的解决方案,以锁定和管理锁定例外?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆