您的位置:首页 > 其它

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

2011-08-21 21:02 761 查看
背景

关于它的名字

解决的问题

设计分析

代码展示

设计缺陷

背景

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

关于它的名字

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

解决的问题

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

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

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

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

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

设计分析

InitJob与RunJob

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

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

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

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

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

uisng(varstate=mLockContext.Run(null))
{
if(state.Failed)
thrownewCustomizedException();
}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

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

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

using(varstate=mLockContext.Run(null))
{
//...
}

如何使用

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

例如:

classAControl:Control
{
privatereadonlyLockContextmLockContext=newLockContext();
publicboolInitializeData()
{
using(varstate=mLockContext.Init(null))
{
if(state.Failed)
returnfalse;
//initializingcode
}
}
publicvoidMethodA()
{
using(varstate=mLockContext.Run(null))
{
if(state.Failed)
return;
//codeformethodahere
}
}

publicvoidMethodB()
{
using(varstate=mLockContext.Run(null))
{
if(state.Failed)
return;
//codeformethodbhere
}
}
}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

代码展示

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Diagnostics;
usingSystem.Threading;

namespaceOpenCourse.OpenActivity.Windows.Views
{
publicclassLockContext
{
privatevolatileboolmInitStarted=false;
privatevolatileboolmInitCompleted=false;
privateobjectmLockObj=newobject();
privatereadonlyintmLockTimeInSeconds=5;

publicLockContext(intlockTimeInSeconds)
{
mLockTimeInSeconds=lockTimeInSeconds;
}

publicLockContext(){}

publicclassInitJob:IDisposable
{
LockContextmContext;
publicboolFailed
{
get;
privateset;
}

publicInitJob(boolstate)
{
Failed=!state;
}
publicInitJob(LockContextcontext)
:this(true)
{
mContext=context;
mContext.mInitStarted=true;
mContext.mInitCompleted=false;
}

#regionIDisposableMembers

publicvoidDispose()
{
if(mContext!=null)
{
mContext.mInitCompleted=true;
mContext.mInitStarted=false;
Monitor.Exit(mContext.mLockObj);
}
}

#endregion
}

publicInitJobInit(stringjobDescription)
{
if(!string.IsNullOrEmpty(jobDescription))
Trace.WriteLine(jobDescription+"trytoenterthelock");

if(Monitor.TryEnter(mLockObj,mLockTimeInSeconds*1000))
{
if(!string.IsNullOrEmpty(jobDescription))
Trace.WriteLine(jobDescription+"enterthelocksuccessfully");

returnnewInitJob(this);
}
else
{
if(!string.IsNullOrEmpty(jobDescription))
Trace.WriteLine(jobDescription+"implementationfailsduetothelockissue");
returnnewInitJob(false);
}
}

publicclassRunJob:IDisposable
{
publicboolFailed{get;privateset;}
privateLockContextmContext;
publicRunJob(boolstate)
{
Failed=!state;
}

publicRunJob(LockContextcontext)
:this(true)
{
mContext=context;
}

#regionIDisposableMembers

publicvoidDispose()
{
if(mContext!=null)
Monitor.Exit(mContext.mLockObj);
}

#endregion
}

publicRunJobRun(stringjobDescription)
{
if(!mInitStarted&&!mInitCompleted)
{
returnnewRunJob(false);
}

if(!string.IsNullOrEmpty(jobDescription))
Trace.WriteLine(jobDescription+"triestoenterthelock");

if(Monitor.TryEnter(mLockObj,mLockTimeInSeconds*1000))
{
if(!string.IsNullOrEmpty(jobDescription))
Trace.WriteLine(jobDescription+"enterthelocksuccessfully");

returnnewRunJob(this);
}
else
{
if(!string.IsNullOrEmpty(jobDescription))
Trace.WriteLine(jobDescription+"implementationfailsduetothelockissue");
returnnewRunJob(false);
}

}

publicvoidResetState()
{
mInitCompleted=false;
mInitStarted=false;
}
}
}

设计缺陷

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

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


.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: