STA and MTA

这篇文章摘自网络。

本文讨论在.NET中使用进程内COM组件时的公寓模型,以一个示例直观演示STAThread和MTAThread的作用和区别。

1. COM中的公寓

1.1 基本规则

公寓是COM组件的运行环境,日常生活中公寓是用来住人的,COM中的公寓是用来住COM组件的对象的,每个COM对象必须且只能位于一个公寓中:单线程公寓(STA)或多线程公寓(MTA)。

每个进程可以有0或多个STA。

每个进程可以有0或1个MTA。

一个线程只能关联到一个公寓。因此所有关联到MTA的线程都是关联到进程唯一的一个MTA。

本线程访问与本线程关联的STA中的COM对象不需要列集,直接访问。

其他线程对STA中的COM对象的访问需要列集(marshal),通过列集,自动实现了多线程访问下的同步

所有线程对MTA中的COM对象的访问不需要列集,直接访问,需要COM组件自身实现多线程下的同步

(列集就是将函数调用序列化,实现跨边界调用,在Windows中通常是通过消息机制实现。在COM中RPC就是列集,在WinForm中Control.Invoke就是一种列集,Remoting也是列集,WCF也是列集,最近流行的RESTfull也是。。。)

1.2 公寓类型匹配

一个COM对象所属的公寓,由两个地方的配置确定:组件公寓模型客户端线程公寓模型

  1. 组件公寓模型是在组件注册到注册表时设定,通过组件公寓模型,组件声明自己可以住在什么样的公寓里。可选项包括:Apartment,Free和Both。Apartment,我只能住在单线程公寓中;Free,我只能住在多线程公寓中;Both,我随意,单线程公寓或多线程公寓都可以。
  2. 客户端线程公寓模型就是线程的公寓模型,表示当前线程提供什么样的公寓。可选项包括:单线程公寓(STA)或多线程公寓(MTA),也就是本文所讨论的STAThread和MTAThread。

下表列出了组件对象最终会住在什么公寓中的组合表:

客户端线程公寓模型 \ 组件公寓模型  Apartment Free Both
STA STA MTA STA
MTA STA MTA MTA

如果组件公寓模型为Apartment,不管客户端线程公寓模型是什么,组件最后都住在STA中,因为组件说了“我只能住在单线程公寓中”。如果当前线程是MTA,COM库会后台创建一个STA来放该组件的对象。

如果组件公寓模型为Free,不管客户端线程公寓模型是什么,组件最后都住在MTA中,因为组件说了“我只能住在多线程公寓中”。如果当前线程是STA,COM库会检查当前进程的MTA有没有创建,没有就创建进程的MTA,然后将组件的对象放在MTA中。

如果组件公寓模型为Both,组件最后都住在与当前线程关联的公寓中,如果当前线程是STA,它就住在STA中;当前线程是MTA,它就住在MTA中。本文中,我们会创建一个并注册一个Both类型的组件,然后分别在STA和MTA中创建该组件的对象。

1.3 .NET中设置客户端线程公寓模型 

在.NET中使用COM组件时,需要设置线程的公寓模型。

在.NET中可以通过STAThread和MTAThread属性来设置主线程的公寓类型, 通过Thread.SetApartmentState可以设置工作线程的公寓类型。

对于WinForm或WPF应用程序,主线程的公寓模型必须为STA,因为用户界面对象都不是线程安全的。

对于控制台应用程序,主线程的公寓模型可以随意设置,为了方便,我们用控制台应用程序来演示。(用WinForm也完全可以演示,只是需要在工作线程中创建组件的对象。)

2. 一个简单的COM组件

为了演示单线程公寓和多线程公寓的区别,我们用ATL实现定义一个简单的COM组件SimpleCom,该组件包含一个返回字符串的方法Hello,返回的字符串分三步合成,每步之间通过Consume方法来消耗较长CPU周期,确保Hello不会在操作系统的一个时间片内被执行完成,保证Hello函数被并发执行,以达到演示的效果。代码如下: Continue reading “STA and MTA”

ODBC, OLEDB, ADO, ADO.Net简史

这篇文章复制自网络。

1. 演变历史

它们是按照以下顺序逐步出现的,史前-> ODBC-> OLEDB-> ADO-> ADO.Net。

看看Wiki上的MDAC定义:“Microsoft Data Access Components(MDAC)是微软专门为数据访问功能而发展的应用程序开发接口,做为微软的统一化数据访问(Universal Data Access; UDA)解决方案的核心组成,最初的版本在1996年时发表,其组成组件有ODBC,OLE DB以及ADO,其中ADO是在Visual Basic上唯一的数据访问管道,而OLE DB则是基于COM之上,供C/C++ 访问与提供数据的接口,ODBC则是统一化的数据访问API。”

也就是说,ODBC, OLE DB, ADO都是这个解决方案的组件。

再看看这张调用图,很多事情就不言自明了。(其实我觉得这图有些不准确,.Net Managed Provider应该有条专线直通Data Stores, 这样才能体现出Sql Server的性能优越性嘛…)
Continue reading “ODBC, OLEDB, ADO, ADO.Net简史”

抽象工厂模式(Abstract Factory Pattern)-创建型模式第二篇

解决的问题:

  1. 如何创建一个类,使其能够独立于它所需要的对象
  2. 如何让相关的产品对象能够一同被创建

怎么去解决:

  1. 将一系列具体对象的创建封装在单独的工厂对象里,你需要定义这个工厂的接口,然后实现这个抽象的工厂
  2. 客户端上下文在调用时,通过工厂去创建抽象的产品从而代替直接创建具体的产品对象

定义:

我想抽象工厂的核心定义是提供一个接口,用于创建一组依赖的对象(接口),并且无需指定具体的实现对象

具体的工厂决定了怎样的一系列具体对象会被创建,方法所返回的将是一个抽象的产品接口类型。在运行时上下文中,客户端将依赖于这个抽象的产品接口指针,而不用关心具体的实现提供者是什么。 Continue reading “抽象工厂模式(Abstract Factory Pattern)-创建型模式第二篇”

工厂方法模式(Factory Method)-创建型模式第一篇

解决的问题:

  1. 如何创建一个对象,以便子类可以重新定义要实例化的类
  2. 一个类如何将实例化延迟到子类

怎么去解决:

  1. 为创建对象定义单独的操作方法(工厂方法)
  2. 通过调用工厂方法创建对象

定义:

定义用于创建对象的接口,但让子类决定要实例化哪个类。工厂方法允许类延迟实例化它使用的子类。

创建对象通常需要复杂的过程,对象的创建可能导致代码的大量重复,可能需要构成对象无法访问的信息,可能无法提供充分的抽象级别。工厂方法模式通过定义单独的方法去创建对象来处理这些问题,然后子类可以重写以满足未来创建产品的派生类型。 Continue reading “工厂方法模式(Factory Method)-创建型模式第一篇”

《设计模式》系列序——写给未来的自己

老骥伏枥,志在千里;烈士暮年,壮心不已。

起伏间,已然离开软件行业八月有余,斯余生不知是否仍有机遇重返程序员行列,遂作《设计模式》系列以祭之。

在上世纪的经典设计模式著作《设计模式 可复用面向对象软件的基础》中将设计模式进行了3个层面的划分,它们分别是创建型设计模式(Creational patterns)、结构型设计模式(Structural patterns)和行为型设计模式(Behavioral patterns),在本系列中我也将按照这种方式去划分。至于为什么选择设计模式去纪念过去十年的编程岁月,下面是鄙人一些心得体会,与大家分享:

  • 天下武学,万变不离其宗,宗于何处?大道三千,殊途同归,归于何处?所谓模式,是对过往的继承和沉淀,从而得出的一些规律性经验。软件设计模式,也同样是前贤们在处理各种问题时所发现的一些共同的东西,这些是宝贵的经验,这些经验可以用来解决很多看起来似乎很复杂的问题,使系统变得更加健壮、易维护、可扩展。并且有意思的是这些模式不仅仅只体现在软件世界中,也是对现实世界的总结与抽象。比如中介者模式,它就像是对现实世界中中介类问题的一个抽象,你需要货,我有货,你不知道我有货,我不知道你需要货,但中介者知道我有货,也知道你需要货,中介者掌握着资源与手段,它从而肩负起你我之间沟通的能力,而你我或许永远也不知道对方。
  • 软件工程里有一个非常重要的思想叫做面向对象思想,从而扩散出很多理论和方法,比如面向对象分析OOA,面向对象设计OOD,面向对象编程OOP,然而在这世间万物中,面向对象又何止仅仅存在于软件世界,面向对象存在于真实世界的每个角落。在面向对象的世界里,万物皆为对象,对象又由二种东西来组成:属性和行为。比如鸟儿有嘴巴,所以可以吃虫,鸟儿就是对象,嘴巴就是属性,吃虫就是行为。而在设计模式的每一个角落,无不充斥着面向对象思想,没有面向对象,就不会产生设计模式。
  • 设计模式+面向对象,是最易学的,也是最难学的。在写这篇文章的时候,我接触它们也已10年,10年前我说它们就是这样,10年后我还是说它们就是这样,对于我自己来说似乎没有看到自己的成长,我非常希望能够通过书写这一系列文章,从而发现另一个自己。

下面来列一下本系列的文章大纲,总计约23篇设计模式文章,我应当每月会更新一篇,也就总计需要两年时间,希望在那最终完成时,自己还能保持着这份程序员的热情。

创建型模式,共有五篇。

结构型模式,共有七篇。

  • 桥接模式 (Bridge Pattern)
  • 适配器模式 (Adapter Pattern)
  • 装饰者模式 (Decorator Pattern)
  • 组合模式 (Composite Pattern)
  • 享元模式 (Flyweight Pattern)
  • 外观模式 (Façade Pattern)
  • 代理模式 (Proxy Pattern)

行为型模式,共有十一篇。

  • 模版方法模式(Template Method)
  • 策略模式(Strategy Pattern)
  • 状态模式(State Pattern)
  • 观察者模式(Observer Pattern)
  • 职责链模式(Chain of responsibility)
  • 命令模式(Command Pattern)
  • 中介者模式(Mediator Pattern)
  • 备忘录模式(Memento Pattern)
  • 迭代器模式(Iterator Pattern)
  • 解释器模式(Interpreter Pattern)
  • 访问者模式( Visitor Pattern)

一个自动保存邮件附件的服务

刚到这个工厂工作的时候,这个工厂的一个应用需要将某个发件人发来的邮件中的附件保存在指定的目录下,以便另外一个服务去指定目录下读取这个附件。不过有一个问题是这个应用一直运行的不是很稳定,有的时候它并不能将附件正确保存,并且占用资源较大,后来也引起了一个生产问题。在后来的某个空闲的时刻,便写了这样一个windows service,目前该服务已经稳定运行三月有余,它具有以下功能:

  1. 支持多个邮箱账号
  2. 支持将邮箱中某个文件夹下的附件保存到指定位置
  3. 支持2种收邮件的协议:POP3和IMAP,以及支持SSL传输
  4. 如果某个邮件附件已经保存,会将该邮件标记为已读

由于是windows service,它没有UI界面,直接来看一下它的config file. Continue reading “一个自动保存邮件附件的服务”

B1 Xml Firing 一个极速解决B1iSN daily issue的工具

这是怎样的一个工具?这是一个极速解决你99.99% B1iSN daily issues的工具,会极大的提高你的工作效率。说实话,如果不考虑业务上的限制,这个工具,理论上可以操作任何B1数据。

我们知道在B1iSN的日常问题处理中,作为一个系统集成平台,B1iSN基本上就是将数据从源系统集成到目标系统中。在B1iSN中,通过XML来描述源系统与目标系统的数据转换,下面是一个B1iSN的XML消息实例。 Continue reading “B1 Xml Firing 一个极速解决B1iSN daily issue的工具”

File Name Length, 检查文件路径长度的小工具

之所以当时写这个小工具的目的,就是为了解决一些由某些软件生成的文件路径过长(尤其印象深刻的是npm中的资源嵌套),同时由于Windows系统的限制,这些文件可能无法被另外的一些应用所访问,所以就想通过这个小工具来预先检测出那些超过系统限制的文件,然后再逐步解决问题。

Tips: 在Windows系统中,目录名长度要小于248字符(最多247个字符),包括盘符“D:\”(3个字符),目录和目录之间“\”(1个字符,目录和文件之间的“\”不算长度),再加上文件名必须小于260字符(最多259个字符,目录名短,文件名就可以长,目录名长,文件名就要短,总共两者加在一起要小于260个字符)

这个小工具有以下几个功能:

  1. 较高的性能,IO访问一直是软件开发中的一个需要注意的性能瓶颈,为了克服这个问题,在这个小工具中,能够通过自定义线程数来提升访问IO的并发性能(建议可以设置成与CPU的核心数相同,超线程可以*2)
  2. 结果窗口会反馈满足条件的文件路径和当前长度

这个小工具的界面如下:

  1. 主界面
  2. Submit,选择文件夹路径
  3. 点击OK,开始处理

Compactor 批量压缩小工具

在发表这个小工具之前,先说一段小故事。当年在某公司的软件框架组里,每当有新的Release要发布时,总是要将很多Components分别打包压缩上传(每个Component皆是独立的功能组件),实在是烦之又烦,于是乎狠心之下写了这样一个批量打包的工具。它具有以下几个功能:

  1. 支持按文件夹分别压缩打包(每个顶层文件夹会被压缩成一个压缩包,当然也可选择只生成一个压缩包)
  2. 支持两种压缩格式(7z和zip)
  3. 支持三种压缩级别(Normal, Fast 快但压缩比低, High 慢但压缩比高)

下面是操作界面:

  1. 假设现在的目录结构如下,我们需要将b、c两个文件夹分别压缩给不同的客户,并且还需要将b、c两个文件夹压缩在一起作个备份。
  2. 打开小工具主界面(原计划还要再添加几个其它独立功能,所以主界面上当时就设计成一个大大的打开Compactor的按钮)
  3. Compactor的主界面

    1. 需要打包的文件夹目录
    2. 选择需要打包的子目录
    3. 选择打包方式(每个子目录压缩成一个压缩包,还是所有子目录放在一起压缩成一个压缩包)
    4. 选择压缩包文件存放目录
    5. 选择压缩包文件格式(7z, zip)
    6. 选择压缩比
    7. 生成压缩包
  4. 生成压缩包,弹出处理信息
  5. 这是当选择将所有文件夹压缩在一个压缩包中的界面
  6. 最后,文件夹中的结果

DoubleColorBall 双色球号码预测小工具

那一年想发财,于是写了这样一个预测下一期双色球中奖号码的小工具。它具有以下几个功能。

  1. 每天自动同步最新的双色球中球号码到本地(也可以手工触发)
  2. 可以手工添加某一期的中奖号码到历史池,也可修改。
  3. 根据历史池中的所有中奖号码记录,计算下一期的可能中奖号码
  4. 每天自动将下期可能中奖号码发送到指定的邮箱列表(也可手动触发)

下面是工具界面:

  1. 主界面
  2. 查询最近号码,可以查看最近100期的中奖号码
  3. 手动添加某期中奖号码
  4. 修改某期中奖号码
  5. 预测可能中奖号码,可以预测5组。
  6. 修改邮件接收人:

Continue reading “DoubleColorBall 双色球号码预测小工具”