热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

从一个实际的例子来了解线程套间模型(.NETandCOMinterop)

问题实例:前些天有一个开发者在微软MSDN.NET开发者论坛上问了一个这样的问题:“IcreatemultipleinstancesofMSScript

问题实例:

前些天有一个开发者在微软MSDN .NET开发者论坛上问了一个这样的问题:

I create multiple instances of MSScriptControlClass on on different threads.

Calling an ExecuteStatement on one thread blocks other instances of MSScriptControlClass from executing on their own thread. I have an old Delphi Binary that seems to allow this behavior. I assume they use the same COM object.

Any ideas?

Thanks in advance.

大概意思是说他创建了一个.NET的程序,并且在多个线程上都创建了MSScriptControlClass的实例,但问题是任意一个线程上的脚本执行都会阻止其他线程的脚本执行。并且他曾经用Delphi使用这个COM组件的时候并没有这个问题。

问题重现:

为了重现这个问题,我用下面的步骤做了一个简单控制台程序来进行测试:

  1. 在Visual Studio里创建了一个C#的控制台程序。
  2. 添加了一个名为”Microsoft  Script Control 1.0”的COM引用。
  3. 添加了下列代码:

static void Main(string[] args){ThreadStart ts = new ThreadStart(LongRunningTask);Thread t = new Thread(ts);t.Start();ThreadStart ts1 = new ThreadStart(LongRunningTask1);Thread t1 = new Thread(ts1);t1.Start();}static void LongRunningTask(){MSScriptControl.ScriptControlClass scc = new MSScriptControl.ScriptControlClass();scc.Language = "Vbscript";scc.AllowUI = true;scc.ExecuteStatement(Properties.Resources.command1);}static void LongRunningTask1(){MSScriptControl.ScriptControlClass scc = new MSScriptControl.ScriptControlClass();scc.Language = "Vbscript";scc.AllowUI = true;scc.ExecuteStatement(Properties.Resources.command2);}

 

以下是Properties.Resources.command1和command2的内容:

commnd1:

For i=1 To 100 Step 1

if i mod 10 = 0 Then MsgBox(i)

Next

command2:

For i=100 To 200 Step 1

if i mod 10 = 0 Then MsgBox(i)

Next

 

期望的结果是有两个消息框弹出,并且一个显示10而另一个显示100. 那样就能证明脚本的执行可以并行进行。

但是得到的结果却是,要么弹出的是10,要么弹出的是100. 也就是说,线程间确实受到了影响。

问题分析:

这到底是为什么呢?如何来解决这个问题呢?

第一个印入我脑海的是:不同的threads之间对COM 对象的操作被进了了同步保护。所以让我想到了COM 线程套间模型。

先简单介绍一下COM线程套间模型(用来保证COM的线程安全):

单线程套间模型(STA):我们可以通过调用CoInitialize 或者CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)来创建STA。 在.NET中的话,CLR 运行时会根据我们的设置去调用相应的方法,并不需要我们显示调用。每个调用了CoInitialize的线程都会有一个独立的套间。

多线程套间模型(MTA):我们可以通过调用CoInitializeEx(NULL, COINIT_MULTITHREADED) 来创建MTA。在.NET中的话,CLR 运行时会根据我们的设置去调用相应的方法,并不需要我们显示调用。所有调用了CoInitializeEx(NULL, COINIT_MULTITHREADED)的线程共享同一个套间。

根据上述描述,多线程套间模型是最有可能出现线程间同步控制的。那么我们来验证一下我们在.NET测试程序中创建的线程是否是MTA。这里我有必要强调一下.Net中托管线程的线程模型。在.NET中,线程模型不是.NET本身的线程安全/线程同步机制,纯粹是为了和之前的线程套间模型兼容。.NET本身是用完成端口、锁、互斥量等等来完成线程同步/线程安全的。

我们只需要在每个线程中调用下面的代码就可以看到当前的设置:

Console.WriteLine(Thread.CurrentThread.GetApartmentState().ToString());

得到的结果是:

主线程:MTA

线程t:  Unknown

线程 t1: Unknown

 

出现Unknown是因为我并没有显示指定用那种线程模型,所以我们得到了默认的Unknown。不过CLR运行时在Unknown的情况下也是调用了CoInitializeEx(NULL, COINIT_MULTITHREADED),这就意味着线程t和t1都是MTA。现在我们就解释了为什么两个线程会出现互相阻塞的情况。

那么STA模式将是我们解决问题的关键。但是不要急,因为这个COM组件并不一定支持STA,我们需要进一步验证。

Windows SDK提供了一个工具叫做OLE/COM Object Viewer, 那么我们就来看看我们需要调用的接口是否支持。下图就是我找到的关键接口。大家可以清楚的看到高亮的部分显示的是Both,那就是说STA和MTA都支持。

                       

问题解决:

那么我们接下来试一下是不是STA可以解决问题,我在之前创建线程的代码的基础上做出了以下修改:

ThreadStart ts = new ThreadStart(LongRunningTask);Thread t = new Thread(ts);t.SetApartmentState(ApartmentState.STA);t.Start();ThreadStart ts1 = new ThreadStart(LongRunningTask1);Thread t1 = new Thread(ts1);t1.SetApartmentState(ApartmentState.STA);t1.Start();

  

尝试再次运行这个控制台程序,我们会发现这次同时出现了显着着10和100的两个消息框。

结论及注意事项:

通过对上述问题的分析与解决,希望大家可以认识:

  1. 在多线程.NET程序中调用COM组件的时候,一定要注意线程套间模型的设置。
  2. 对于Scriptcontrol来说,它的开发者实现了多线程调用的线程同步和线程安全策略。但是并不意味着所有的COM组件都不能在MTA下进行多线程并行操作,这也是取决于COM服务是如何实现的。
  3. 我们可以通过OLE/COM Object Viewer来查看某个COM服务支持那种线程套间模型,并且对调用线程进行相应设置。

 

相关文档参考:

Understanding and Using COM Threading Models

http://msdn.microsoft.com/en-us/library/ms809971.aspx

INFO: Descriptions and Workings of OLE Threading Models

http://support.microsoft.com/kb/150777

Processes, Threads, and Apartments

http://msdn.microsoft.com/en-us/library/windows/desktop/ms693344(v=vs.85).aspx

Managed and Unmanaged Threading

http://msdn.microsoft.com/en-us/library/5s8ee185.aspx

COM Clients and Servers

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683835(v=vs.85).aspx

An Overview of Managed/Unmanaged Code Interoperability

http://msdn.microsoft.com/en-us/library/ms973872.aspx


转载于:https://www.cnblogs.com/alanyao/p/3370168.html


推荐阅读
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 深入解析 Android IPC 中的 Messenger 机制
    本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ... [详细]
  • ListView简单使用
    先上效果:主要实现了Listview的绑定和点击事件。项目资源结构如下:先创建一个动物类,用来装载数据:Animal类如下:packagecom.example.simplelis ... [详细]
  • 在寻找轻量级Ruby Web框架的过程中,您可能会遇到Sinatra和Ramaze。两者都以简洁、轻便著称,但它们之间存在一些关键区别。本文将探讨这些差异,并提供详细的分析,帮助您做出最佳选择。 ... [详细]
  • 2017-2018年度《网络编程与安全》第五次实验报告
    本报告详细记录了2017-2018学年《网络编程与安全》课程第五次实验的具体内容、实验过程、遇到的问题及解决方案。 ... [详细]
  • [Vue.js 3.0] Guide – Scaling Up – State Management
    [Vue.js 3.0] Guide – Scaling Up – State Management ... [详细]
  • 本文详细介绍了如何在现有的Android Studio项目中集成JNI(Java Native Interface),包括下载必要的NDK和构建工具,配置CMakeLists.txt文件,以及编写和调用JNI函数的具体步骤。 ... [详细]
  • 采用IKE方式建立IPsec安全隧道
    一、【组网和实验环境】按如上的接口ip先作配置,再作ipsec的相关配置,配置文本见文章最后本文实验采用的交换机是H3C模拟器,下载地址如 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 本文将详细探讨 Java 中提供的不可变集合(如 `Collections.unmodifiableXXX`)和同步集合(如 `Collections.synchronizedXXX`)的实现原理及使用方法,帮助开发者更好地理解和应用这些工具。 ... [详细]
  • 本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ... [详细]
  • 本文详细介绍了虚拟专用网(Virtual Private Network, VPN)的概念及其通过公共网络(如互联网)构建临时且安全连接的技术特点。文章探讨了不同类型的隧道协议,包括第二层和第三层隧道协议,并提供了针对IPSec、GRE以及MPLS VPN的具体配置指导。 ... [详细]
  • 本文探讨了在iOS平台上开发BLE(蓝牙低功耗)应用程序时遇到的挑战,特别是如何实现应用在后台模式下仍能持续扫描并连接蓝牙设备。文章提供了具体的配置方法和常见的问题解决方案。 ... [详细]
  • 俗话说得好,“工欲善其事,必先利其器”。这句话不仅强调了工具的重要性,也提醒我们在任何项目开始前,准备合适的工具至关重要。本文将介绍几款C语言编程中常用的工具,帮助初学者更好地选择适合自己学习和工作的编程环境。 ... [详细]
author-avatar
手机用户2502920725
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有