• 同步处理(LockContext),期待大家的意见 CQ


    背景

    关于它的名字

    解决的问题

    设计分析

    代码展示

    设计缺陷

     

    背景

    最近由于要处理很多同步的问题,所以写了不少这方面的代码。最为显著的有已经在blog上提到的Object Cache。还有接下来要向大家展示的LockContext。

    关于它的名字

    首先,LockContext这个名称是否合适还值得商榷,因为这里面使用了Lock,但其解决的问题当某一个对象还未完成初始化时,所有被其处理的事件通知都要等待。关于名称,我没有多想,因为我的目的是为了解决当前的问题。所以,当你读完代码时,有了新的想法,欢迎你与我分享。

    解决的问题

    当控件的实例被创建后,控件开始了数据的初始化任务,由于控件需要加载的数据量大,由于网络等因素,使数据的加载时间长,因而使得控件的初始化过程耗时较长。

    而在另外一方面,当控件的实例化完成后,控件已经能够事件通知(在我的具体环境下,事件通知是由服务器发出的)。

    因此,问题就出来了:在控件初始化的过程中,处理事件通知,会导致怪异的错误。

    有人可能要问,找到到底是什么样的事件通知导致了错误,这是可以分析的。但我这里的环境是,控件加载的数据量和数据种类多,与此同时,来至于服务器端的事件通知也多,就算是分析清楚了错误,将这个具体的错误解决,也不能保证这样的错误会在今后的维护工作中继续发生。

    因此,就需要通过某种机制解决这个问题,即当控件处于初始化状态时,当前视图访问该控件的其它线程都会被挂起。当控件初始化完成后再执行这些挂起的任务。

    设计分析

    InitJob与RunJob

    该类用于保证初始化线程被锁定。如果InitJob没有被创建或者没有被解锁,那么其它RunJob将不能加锁。当其中一个RunJob加锁后,其余的RunJob只能等待。

    InitJob与RunJob等待锁的时间是有限的,如果在等待的时间内没有加锁成功那么Job实例的Failed将会返回true。这也使得使用该机制的代码能够体面的处理加锁失败问题。

    你可以直接返回,如下面代码

    using(var state = mLockContext.InitJob(null))
    {
        if(state.Failed)
            return;
    }

    你也可以抛出异常来改变程序的流程,如以下代码

    uisng(var state = mLockContext.Run(null))
    {
        if(state.Failed)
            throw new CustomizedException();
    }

    想一个办法来体面地使用Job

    正如上面的代码展示,我选择了using块的方式来体面地使用Job。这样将加锁和解锁过程体面地影藏起来。

    using(var state = mLockContext.Run(null))
    {
        //...
    }
     

    如何使用

    正如“解决的问题”块中提到的,要使用这样的代码,就得将其放在类的初始化方法、其它public方法和事件通知处理方法中,保证该类的入口都有mLockContext“把守”。

    例如:

    class AControl : Control
    {
        private readonly LockContext mLockContext = new LockContext();
        public bool InitializeData()
        {
            using(var state = mLockContext.Init(null))
            {
                if(state.Failed)
                   return false;
                //initializing code
            }
        }
       public void MethodA()
       {
            using(var state = mLockContext.Run(null))
            {
                if(state.Failed)
                   return;
                //code for method a here
             }
        }
    
        public void MethodB()
       {
            using(var state = mLockContext.Run(null))
            {
                if(state.Failed)
                   return;
                //code for method b here
             }
        }
    }

    代码展示

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Diagnostics;
    using System.Threading;
    
    namespace OpenCourse.OpenActivity.Windows.Views
    {
        public class LockContext
        {
            private volatile bool mInitStarted = false;
            private volatile bool mInitCompleted = false;
            private object mLockObj = new object();
            private readonly int mLockTimeInSeconds = 5;
    
            public LockContext(int lockTimeInSeconds)
            {
                mLockTimeInSeconds = lockTimeInSeconds;
            }
    
            public LockContext() { }
    
            public class InitJob : IDisposable
            {
                LockContext mContext;
                public bool Failed
                {
                    get;
                    private set;
                }
    
                public InitJob(bool state)
                {
                    Failed = !state;
                }
                public InitJob(LockContext context)
                    : this(true)
                {
                    mContext = context;
                    mContext.mInitStarted = true;
                    mContext.mInitCompleted = false;
                }
    
    
                #region IDisposable Members
    
                public void Dispose()
                {
                    if (mContext != null)
                    {
                        mContext.mInitCompleted = true;
                        mContext.mInitStarted = false;
                        Monitor.Exit(mContext.mLockObj);
                    }
                }
    
                #endregion
            }
    
            public InitJob Init(string jobDescription)
            {
                if (!string.IsNullOrEmpty(jobDescription))
                    Trace.WriteLine(jobDescription + " try to enter the lock");
    
                if (Monitor.TryEnter(mLockObj, mLockTimeInSeconds * 1000))
                {
                    if (!string.IsNullOrEmpty(jobDescription))
                        Trace.WriteLine(jobDescription + " enter the lock successfully");
    
                    return new InitJob(this);
                }
                else
                {
                    if (!string.IsNullOrEmpty(jobDescription))
                        Trace.WriteLine(jobDescription + " implementation fails due to the lock issue");
                    return new InitJob(false);
                }
            }
    
            public class RunJob : IDisposable
            {
                public bool Failed { get; private set; }
                private LockContext mContext;
                public RunJob(bool state)
                {
                    Failed = !state;
                }
    
                public RunJob(LockContext context)
                    : this(true)
                {
                    mContext = context;
                }
    
                #region IDisposable Members
    
                public void Dispose()
                {
                    if (mContext != null)
                        Monitor.Exit(mContext.mLockObj);
                }
    
                #endregion
            }
    
            public RunJob Run(string jobDescription)
            {
                if (!mInitStarted && !mInitCompleted)
                {
                    return new RunJob(false);
                }
    
                if (!string.IsNullOrEmpty(jobDescription))
                    Trace.WriteLine(jobDescription + " tries to enter the lock");
    
                if (Monitor.TryEnter(mLockObj, mLockTimeInSeconds * 1000))
                {
                    if (!string.IsNullOrEmpty(jobDescription))
                        Trace.WriteLine(jobDescription + " enter the lock successfully");
    
                    return new RunJob(this);
                }
                else
                {
                    if (!string.IsNullOrEmpty(jobDescription))
                        Trace.WriteLine(jobDescription + " implementation fails due to the lock issue");
                    return new RunJob(false);
                }
    
            }
    
            public void ResetState()
            {
                mInitCompleted = false;
                mInitStarted = false;
            }
        }
    }
    

    设计缺陷

    从上面的例子代码可以发现, mLockContext都是被用于public方法。但是,如果method a 调用了method b,那后果是method b不能成功执行,因为锁已经被method a占用了。

    这个设计缺陷我也在想办法解决。欢迎大家的任何建议和意见。

     
  • 相关阅读:
    Delphi中字符串默认以#开头。 dotNET界面
    生成飞库jar手机电子小说ByC# dotNET界面
    CS0016: Could not write to output file 'c:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/ '拒绝访问' dotNET界面
    SVN备份和还原操作特指Window下使用Visual SVN dotNET界面
    生成飞库jar手机电子小说ByC#[2] dotNET界面
    写了个类似按键精灵的找图类。方便大家做UI测试的时候可以用 dotNET界面
    各种中间件
    聊聊位运算吧
    聊聊设计模式
    腾讯云容器服务(TKE集群)新版本config取不到token问题
  • 原文地址:https://www.cnblogs.com/czy/p/2148606.html
Copyright © 2020-2023  润新知