博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
以上下文(Context)的形式创建一个共享数据的容器
阅读量:6327 次
发布时间:2019-06-22

本文共 9853 字,大约阅读时间需要 32 分钟。

在很多情况下我们具有这样的需求:为一组相关的操作创建一个执行上下文并提供一个共享的数据容器,而不是简单地定义一个全局变量,或者将数据通过参数传来传去。这样的上下文一般具有其生命周期,它们在目标操作开始执行的时候被激活,在执行完成之后被回收。该上下文一般不能跨越多个线程,以避免多个线程操作相同的数据容器造成数据的不一致。针对这个需求,我们写了一个非常简单的例子,有兴趣的朋友可以看看。[源代码从下载]

目录

一、ExecutionContext的基本编程方式
二、异步调用的问题
三、ExecutionContext
四、DependentExecutionContext
五、ExecutionContextScope

一、ExecutionContext的基本编程方式

我将这个作为数据容器的上下文命名为ExecutionContext,我完全借鉴了TransactionScope的编程方式来设计这个ExecutionContext。如下的代码片段体现了ExecutionContext最基本的编程方式:我们通过ExecutionContextScope 来创建当前ExecutionContext,并且控制它的生命周期。当前ExecutionContext通过静态属性Current获取。我们分别调用GetValue和SaveValue进行上下文数据项的获取和设置。

using (ExecutionContextScope contextScope = new ExecutionContextScope()){    //Set    ExecutionContext.Current.SetValue(“ActivityID”, “A001”);    //Get    string activityId = ExecutionContext.Current.GetValue
(“ActivityID”)}

和TransactionScope一样,ExecutionContextScope 也支持嵌套。具体来说,当我们采用嵌套的ExecutionContextScope 时,有对应着如下三种不同的上下文共享行为:

  • Required: 外层的ExecutionContext直接被内层使用;
  • RequiresNew:内层创建一个全新的ExecutionContext;
  • Suppress:外层的ExecutionContext在内层中使被屏蔽掉,内层的当前ExecutionContext不存在。

如下的代码片段反映了嵌套使用ExecutionContextScope 的编程方式,上述的三种行为通过作为ExecutionContextScope构造函数参数的ExecutionContextOption枚举来控制。

using (ExecutionContextScope contextScope1 = new ExecutionContextScope()){    //...    using (ExecutionContextScope contextScope2 = new ExecutionContextScope(ExecutionContextOption.))    {        //...    }    using (ExecutionContextScope contextScope2 = new ExecutionContextScope(ExecutionContextOption.))    {        //...    }    using (ExecutionContextScope contextScope2 = new ExecutionContextScope(ExecutionContextOption.))    {        //...    }}

ExecutionContext基本的编程方式,以及三种ExecutionContextScope 嵌套所体现的ExecutionContext创建/共享机制可以通过如下的Unit Test代码来体现:

[TestMethod]public void SetAndGetContexts1(){    string name = Guid.NewGuid().ToString();    string value1 = Guid.NewGuid().ToString();    string value2 = Guid.NewGuid().ToString();    //1. Outside of ApplicationContextScope: ApplicationContext.Current = null    Assert.IsNull(ExecutionContext.Current);    //2. Current ApplicationContext is avilable in the ApplicationContextScope.    using (ExecutionContextScope contextScope = new ExecutionContextScope())    {        ExecutionContext.Current.SetValue(name, value1);        Assert.AreEqual
(value1, ExecutionContext.Current.GetValue
(name)); } //3. Nested ApplicationContextScope: ApplicationContextOption.Required using (ExecutionContextScope contextScope1 = new ExecutionContextScope()) { ExecutionContext.Current.SetValue(name, value1); using (ExecutionContextScope contextScope2 = new ExecutionContextScope(ExecutionContextOption.Required)) { Assert.AreEqual
(value1, ExecutionContext.Current.GetValue
(name)); ExecutionContext.Current.SetValue(name, value2); Assert.AreEqual
(value2, ExecutionContext.Current.GetValue
(name)); } Assert.AreEqual
(value2, ExecutionContext.Current.GetValue
(name)); } //4. Nested ApplicationContextScope: ApplicationContextOption.RequiresNew using (ExecutionContextScope contextScope1 = new ExecutionContextScope()) { ExecutionContext.Current.SetValue(name, value1); using (ExecutionContextScope contextScope2 = new ExecutionContextScope(ExecutionContextOption.RequiresNew)) { Assert.IsNotNull(ExecutionContext.Current); Assert.IsNull(ExecutionContext.Current.GetValue
(name)); ExecutionContext.Current.SetValue(name, value2); Assert.AreEqual
(value2, ExecutionContext.Current.GetValue
(name)); } Assert.AreEqual
(value1, ExecutionContext.Current.GetValue
(name)); } //5. Nested ApplicationContextScope: ApplicationContextOption.Supress using (ExecutionContextScope contextScope1 = new ExecutionContextScope()) { ExecutionContext.Current.SetValue(name, value1); using (ExecutionContextScope contextScope2 = new ExecutionContextScope(ExecutionContextOption.Suppress)) { Assert.IsNull(ExecutionContext.Current); } Assert.AreEqual
(value1, ExecutionContext.Current.GetValue
(name)); }}

二、异步调用的问题

如果具有当前ExecutionContext的程序以异步的方式执行相应的操作,我们希望当前操作和异步操作使用不同的数据容器,否则就会出现并发问题;但是我们又希望在异步操作开始执行的时候,当前的上下文数据能够自动地拷贝过去。为此我们依然借鉴TransactionScope的方式,定义了一个DependentContext(对应着DependentTransaction)。在异步操作开始执行之前,我们根据当前ExecutionContext创建一个DependentContext,此时当前ExecutionContext相应数据项会拷贝到DependentContext中。在异步操作代码中,我们根据DependentContext创建ExecutionContextScope ,那么通过Current属性返回的实际上就是这么一个DependentContext。由于DependentContext和当前ExecutionContext各自具有自己的数据容器,针对它们的操作互不影响。如下所示的相应的编程方式:

using (ExecutionContextScope contextScope1 = new ExecutionContextScope()){    ExecutionContext.Current.SetValue(name, value1);        ExecutionContext.Current.SetValue(name, value2);     Task.Factory.StartNew(() =>        {            using (ExecutionContextScope contextScope2 = new ExecutionContextScope())            {                 string value1 = ExecutionContext.Current.GetValue
(name); } });}

相应的编程方式,已经异步线程和当前线程上下文的独立性也可以通过如下所示的Unit Test代码来体现。

[TestMethod]public void SetAndGetContexts2(){    string name = Guid.NewGuid().ToString();    string value1 = Guid.NewGuid().ToString();    string value2 = Guid.NewGuid().ToString();    //1. Change current ApplicationContext will never affect the DependentContext.    using (ExecutionContextScope contextScope1 = new ExecutionContextScope())    {        ExecutionContext.Current.SetValue(name, value1);        DependentContext depedencyContext = ExecutionContext.Current.DepedentClone();        ExecutionContext.Current.SetValue(name, value2);        Task
task = Task.Factory.StartNew
(() => { using (ExecutionContextScope contextScope2 = new ExecutionContextScope(depedencyContext)) { return ExecutionContext.Current.GetValue
(name); } }); Assert.AreEqual
(value1, task.Result); Assert.AreEqual
(value2, ExecutionContext.Current.GetValue
(name)); } //2. Change DependentContext will never affect the current ApplicationContext. using (ExecutionContextScope contextScope1 = new ExecutionContextScope()) { ExecutionContext.Current.SetValue(name, value1); DependentContext depedencyContext = ExecutionContext.Current.DepedentClone(); Task
task = Task.Factory.StartNew
(() => { using (ExecutionContextScope contextScope2 = new ExecutionContextScope(depedencyContext)) { ExecutionContext.Current.SetValue(name, value2); return ExecutionContext.Current.GetValue
(name); } }); Assert.AreEqual
(value2, task.Result); Assert.AreEqual
(value1, ExecutionContext.Current.GetValue
(name)); }}

三、ExecutionContext

现在我们来讨论具体的设计和实现,先来看看表示当前执行上下文的ExecutionContext的定义。如下面的代码片段所示,ExecutionContext实际上是利用了通过Items属性表示的字典对象作为保存数据的容器,GetValue和SetValue实际上就是针对该字典的操作。表示当前ExecutionContext的静态属性Current实际上是返回一个应用了ThreadStaticAttribute特性的静态字段current,意味着ExecutionContext是基于某个线程的,每个线程的当前ExecutionContext是不同的。方法DepedentClone用于创建DependentContext 以实现当前上下文数据向异步线程的传递。

[Serializable]public class ExecutionContext{        private static ExecutionContext current;    public IDictionary
Items { get; internal set; } internal ExecutionContext() { this.Items = new Dictionary
(); } public T GetValue
(string name, T defaultValue = default(T)) { object value; if (this.Items.TryGetValue(name, out value)) { return (T)value; } return defaultValue; } public void SetValue(string name, object value) { this.Items[name] = value; } public static ExecutionContext Current { get { return current; } internal set { current = value; } } public DependentContext DepedentClone() { return new DependentContext(this); }}

四、DependentExecutionContext

如下所示的DependentContext的定义,它是ExecutionContext的子类。我们我们根据指定的ExecutionContext 对象创建一个DependentContext对象的时候,它的上下文数据项会自动拷贝到创建的DependentContext之中。

[Serializable]public class DependentContext: ExecutionContext{    public Thread OriginalThread { get; private set; }    public DependentContext(ExecutionContext context)    {       this.OriginalThread = Thread.CurrentThread; this.Items = new Dictionary
(context.Items); }}

五、ExecutionContextScope

如下所示的是ExecutionContextScope的定义,它实现了IDisposable接口。在ExecutionContextScope被创建之前,当前ExecutionContext 被保存下来。第一个构造函数根据指定的ExecutionContextOption来对当前ExecutionContext 进行相应的设置;第二个构造函数则直接将指定的DependentContext 作为当前的ExecutionContext 。

public class ExecutionContextScope:IDisposable{    private ExecutionContext originalContext = ExecutionContext.Current;    public ExecutionContextScope(ExecutionContextOption contextOption = ExecutionContextOption.Required)    {        switch (contextOption)        {            case ExecutionContextOption.RequiresNew:                {                    ExecutionContext.Current = new ExecutionContext();                    break;                }            case ExecutionContextOption.Required:                {                    ExecutionContext.Current = originalContext ?? new ExecutionContext();                    break;                }            case ExecutionContextOption.Suppress:                {                    ExecutionContext.Current = null;                    break;                }        }    }    public ExecutionContextScope(DependentContext dependentContext)    {        if (dependentContext.OriginalThread == Thread.CurrentThread)        {            throw new InvalidOperationException("The DependentContextScope cannot be created in the thread in which the DependentContext is created.");        }        ExecutionContext.Current = dependentContext;    }    public void Dispose()    {        ExecutionContext.Current = originalContext;    }}

转载地址:http://jugaa.baihongyu.com/

你可能感兴趣的文章
NetBackup下ORACLE恢复测试方案实例解析
查看>>
【有奖征文】“失业”程序员的苦辣酸甜
查看>>
nagios监控web/mysql多角度实战分享(一)
查看>>
SCOM 2012系列⑦即时消息通知上
查看>>
IE9是如何被FireFox4超越全球市场份额的?
查看>>
linux bunzip2命令
查看>>
敏捷个人:通过实践TOGAF来思考如何学习并应用新的方法?
查看>>
Android系统的开机画面显示过程分析(6)
查看>>
vivo Hi-Fi+QQ音乐 数字音乐市场的一剂良方
查看>>
Cocos2d-x 3.2 异步动态加载 -- 保卫萝卜开发总结
查看>>
聚焦触宝反侵权事件:中国创业者用什么护航海外市场大门
查看>>
AOP技术基础
查看>>
Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析(2)
查看>>
Lync 小技巧-5-当前已暂停共享
查看>>
无线802.11n 2.4G与5G性能测试
查看>>
子域名信息收集攻略
查看>>
[Android]开发数独游戏思路分析过程
查看>>
SpreadJS 类Excel表格控件 - V12 新特性详解
查看>>
理解并取证:IPv6与IPv4在报文结构上的区别
查看>>
小白该如何学习Linux操作系统(2)
查看>>