C# 线程手册 第一章 线程定义 .NET 和 C# 对线程的支持

由于.NET Framework 支持自由线程,所以自由线程在所有.NET 语言中都存在,包括C#和VB.NET. 在下一部分,我们将着重关注如何提供这种支持以及更多关于线程是如何做到的,而不再关注线程是什么。我们将讨论一些能够进一步帮助区分进程的额外支持。

在这一部分的最后,你将理解:

1. 什么是System.AppDomain 类以及它可以帮助你做什么?

2. .NET runtime(运行时)如何监控线程?

System.AppDomain

当我们在这一章的早些时候解释进程时,我们知道进程是对维系进程存在的内存和资源的物理隔离。我们后来说到一个进程至少有一个线程。当初微软设计.NET Framework 时,它又添加了一层称作应用程序域或AppDomain的隔离。应用程序域不是像进程那样的物理隔离;它是进程内部的进一步的逻辑隔离。由于在一个进程中可能有多个应用程序域,所以我们有一些优势。大体上说,对标准进程来说不通过代理访问其他进程的数据是不可能的, 而使用代理会导致重大开销和代码复杂化。然而,通过介绍应用程序域的概念,我们现在可以在一个进程中运行多个程序。进程提供的隔离在应用程序域中也存在。线程可以在不同应用程序域间执行而没有与相关的内部进程通信开销。这些额外的进程内部的壁垒的好处是他们对内部数据提供类型检查。

微软将这些应用程序域相关的所有功能封装到一个System.AppDomain的类中。微软.NET 程序集与这些应用程序域之间有紧密联系。任何时候当一个程序集被加载到一个程序中时,它实际上是被加载到应用程序域中。除非特别情况,程序集都会被加载到调用代码的应用程序域中。应用程序域与线程也有一个直接关系;它们可以有一个或多个线程,就像进程一样。然而,不同点是一个应用程序域可能在进程内部创建而不是通过新的线程创建。这个关系可以简化成如图9所示的模型。

图9

 

在.NET 中,AppDomain和线程类由于安全原因而不能继承。

每个应用程序都包含一个或者多个AppDomains.每个AppDomain可以创建并执行多个线程。下图显示在机器X上有两个操作系统进程Y和Z。操作系统进程Y有四个应用程序域:A,B,C和D。操作系统进程Z有两个应用程序域:A和B。

图10

 

设置AppDomain数据

你已经听了理论看了模型;现在我们要动手写点真正的代码。在下面的例子中,我们将使用AppDomain设置数据,收集数据并确定AppDomain中正在运行的线程。创建一个新的类文件appdomain.cs并输入以下代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace MyAppDomain
{
class MyAppDomain
{
private AppDomain mDomain;
private int mThreadId;

public void SetDomainData(string vName, string vValue)
{
mDomain.SetData(vName, (object)vValue);
//mThreadId = AppDomain.GetCurrentThreadId();
mThreadId = Thread.CurrentThread.ManagedThreadId;
}

public string GetDomainData(string name)
{
return (string)mDomain.GetData(name);
}

static void Main(string[] args)
{
string dataName = "MyData";
string dataValue = "Some Data to be stored";

Console.WriteLine("Retrieving current domain");
MyAppDomain obj = new MyAppDomain();
obj.mDomain = AppDomain.CurrentDomain;

Console.WriteLine("Setting domain data");
obj.SetDomainData(dataName, dataValue);

Console.WriteLine("Getting domain data");
Console.WriteLine("The data found for key " + dataName
+ " is " + obj.GetDomainData(dataName)
+ " running on thread id: " + obj.mThreadId);
}
}
}

你的输出应该类似于:

这即使对于非C#开发人员来说也很直观。然而,让我们看一下代码并确定究竟发生了什么。这是这个类的第一个重要地方:

public void SetDomainData(string vName, string vValue)
{
mDomain.SetData(vName, (object)vValue);
//mThreadId = AppDomain.GetCurrentThreadId();
mThreadId = Thread.CurrentThread.ManagedThreadId;
}

这个方法把设置名字和值的数据作为参数。你将会注意到SetData() 方法当传递参数时已经做了一些不同的事情。这里我们将字符串转换成一个Object类型,因为SetData()的第二个参数类型是object. 由于我们仅使用一个字符串和一个继承自System.Object的字符串,我们可以直接使用这个变量而不用把它强制转换为一个对象。然而,其他的你想要存储的数据可能不像现在这个容易处理。这个事实简单地提醒我们已经完成了这个转换。在这个方法的最后部分,你将注意到我们可以通过对AppDomain对象的GetCurrentThreadId属性的简单调用获取当前执行线程的ID(已经过时,新方法是 Thread.CurrentThread.ManagedThreadId)。

让我们继续下一个方法:

public string GetDomainData(string name)
{
return (string)mDomain.GetData(name);
}

这个方法也很基础。我们使用AppDomain类的GetData()方法获取一个基于键值的数据。在这种情况下,我们仅是将GetDomainData()方法的参数传递给GetData()方法。我们将GetData方法的结果返回。

最后,让我们看一下主方法:

static void Main(string[] args)
{
string dataName = "MyData";
string dataValue = "Some Data to be stored";

Console.WriteLine("Retrieving current domain");
MyAppDomain obj = new MyAppDomain();
obj.mDomain = AppDomain.CurrentDomain;

Console.WriteLine("Setting domain data");
obj.SetDomainData(dataName, dataValue);

Console.WriteLine("Getting domain data");
Console.WriteLine("The data found for key "
+ dataName
+ " is " + obj.GetDomainData(dataName)
+ " running on thread id: " + obj.mThreadId);
}

我们通过初始化我们想在AppDomain中存储的名值对开始并向控制台写一段代码提示我们方法已经开始执行。下一步,我们使用对当前执行的AppDomain对象(Main()方法中执行的那个对象)的引用对我们类中的Domain字段赋值。下一步我们调用方法-传递参数给SetDomainData()方法:

obj.SetDomainData(dataName, dataValue);

继续,我们向GetDomainData()方法传递一个参数来获取我们刚设置的数据并把它插入到控制台输出流中。我们也输出我们的类的ThreadId属性来看当前调用方法的ThreadId.

在一个特定AppDomain中执行代码

现在让我们看一下如何创建一个新的应用程序域并观察当在新创建的AppDomain中创建线程时的重要行为。下面代码包含在create_appdomain.cs中:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace MyAppDomain
{
public class CreateAppDomains
{
static void Main(string[] args)
{
AppDomain domainA = AppDomain.CreateDomain("MyDomainA");
string stringA = "DomainA Value";
domainA.SetData("DomainKey", stringA);

CommonCallBack();

CrossAppDomainDelegate delegateA =
new CrossAppDomainDelegate(CommonCallBack);
domainA.DoCallBack(delegateA);
}

static void CommonCallBack()
{
AppDomain domain = AppDomain.CurrentDomain;
Console.WriteLine("The value '" + domain.GetData("DomainKey")
+ "' was found in " + domain.FriendlyName
+ " running on thread id: "
+ Thread.CurrentThread.ManagedThreadId);
}
}
}

编译类的输出看起来应该像这样:

你会注意到我们在这个例子中创建了两个应用程序域。我们调用了AppDomain类中的静态CreateDomain()方法。构造函数参数是我们创建的AppDomain的一个友好名字。稍后我们将看到可以通过一个只读属性访问这个友好名字。下面是创建AppDomain实例的代码:

AppDomain domainA = AppDomain.CreateDomain("MyDomainA");

下一步我们调用先前例子中的SetData()方法。由于之前已经介绍过这个方法所以这里就略过。然而,我们需要解释的是如何在一个给定的AppDomain中获得代码运行。通过AppDomain类中的DoCallBack()方法可以实现。这个方法将一个CrossAppDomainDelegate作为它的参数。在这种情况下,我们已经创建了一个CrossAppDomainDelegate的实例并将它的名字作为参数传递给我们希望执行的构造函数中去。

CommonCallBack();

CrossAppDomainDelegate delegateA = new CrossAppDomainDelegate(CommonCallBack);
domainA.DoCallBack(delegateA);

我们首先调用CommonCallBack()。这是要在主AppDomain的上下文中执行CommonCallBack() 方法。你将看到输出的是主AppDomain的FriendlyName属性是执行者名字。

最后,看一下CommonCallBack()方法本身:

static void CommonCallBack()
{
AppDomain domain = AppDomain.CurrentDomain;
Console.WriteLine("The value '" + domain.GetData("DomainKey")
+ "' was found in " + domain.FriendlyName
+ " running on thread id: "
+ Thread.CurrentThread.ManagedThreadId);
}

你会发现它非常原子化以至于不论在什么实例下运行都会工作。我们再次使用CurrentDomain属性获取执行代码的应用程序域的一个引用。然后我们再次使用FriendlyName属性确定我们在使用哪个AppDomain.

我们也调用了GetCurrentThreadId()方法(已过时,同上)。当你查看输出,你将看到不论我们在哪个AppDomain中执行都会得到同样的线程ID。需要知道不论一个AppDomain有没有线程,线程都可以跨应用程序域执行,这是很重要的。

线程管理和.NET运行时

.NET Framework 提供比进程自由线程和逻辑应用程序域还有多的特性。事实上,.NET Framework 提供对处理器线程的对象表示。这些对象表示是System.Threading.Thread类的实例。我们将在下一章深入探讨。然而,再继续下一章之前,我们必须了解非托管线程托管线程是如何关联的。那就是说,非托管线程(在.NET 世界之外创建的线程)实例有关,后者表示运行在.NET CLR 中的线程。

.NET 运行时监控所有由.NET代码创建的线程。它也监控所有可能在托管代码中执行的非托管线程。由于托管代码可以通过COM-可调用包装暴露,所以非托管线程运行在.NET运行时中是可能的。

当非托管代码运行在一个托管线程中,运行时将会检查一个托管线程对象的TLS是否存在。如果找到了一个托管线程,运行时就会使用它。否则它将创建一个新的然后使用。这很简单,但是需要注意。我们仍想要得到一个关于我们线程的对象表示而不管它来自哪里。如果运行时无法管理且为外部调用类型创建线程,我们将无法在托管环境中确定线程,甚至控制它。

关于线程管理最后一件重要的事是一旦非托管调用返回到非托管代码中,运行时将无法继续检测它。

总结

我们在这一章讲了很多内容。关于什么是多任务以及如何通过使用线程实现多任务。知道了多任务和自由线程不是一回事儿。还讲了进程以及如何与其他应用程序隔离。我们也讲述了Windows操作系统中线程方法。你现在知道Windows会将当前线程中断以使其他线程获取一个简单的周期作为运行时间。这个简单的周期称作一个时间片或间歇。我们也描述了线程优先级功能和这些优先级的不同级别,以及线程默认情况下会继承父进程的优先级。

我们也描述了.NET 运行时如何监控在.NET环境中创建的线程以及在托管代码中执行的非托管线程。还描述了.NET Framework对线程的支持。System.AppDomain类在进程物理数据隔离的基础上提供额外层的逻辑数据隔离。我们描述了线程如何轻松地从一个AppDomain到另外一个AppDomain. 还有我们也查看了为何一个AppDomain没有像进程一样有自己的线程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/404540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端第一天 HTML基础

前端第一天 HTML基础 1.是什么 HTML里值得记住的就几个点,第一,这东西还有自身也有结构 这东西是个超文本,可以挂载文字图片视频或者别的超文本自身可以通过各种各样的tag进行标记,排版给浏览器提供渲染的依据, 2.…

view2.0移植自定义图标,带颜色修改

1.下载你的iconfont项目 1.将图标添加到项目,修改font-family值 2.下载项目打包文件,解压后如图所示 我们只关心里面的iconfont.css和iconfont.json 我们在这个文件夹,新建一个convert.js,内容如下 //convert.js let path1 "./iconf…

H3C——路由策略和策略路由实例配置

配置如下:[IP]int s0/2/0[IP-Serial0/2/0]ip add 202.112.1.10 28[IP-Serial0/2/0]int s0/2/1[IP-Serial0/2/1]ip add 61.67.1.10 28 [IP-Serial0/2/1]int lo0[IP-LoopBack0]ip add 10.10.10.10 32[IP]ip route-static 0.0.0.0 0 202.112.1.9 指条静态缺省路由到R1 …

功能:人脉(People Hub)7-固定到“开始”屏幕

如果是您的亲人和密友,再或者是领导和重要客户。 您需要经常沟通,可以将他的联系人头像固定在开始屏幕上,方便您的沟通。方法:很简单,“长按该联系人”后,有菜单出现,选择弹出菜单中的“固定到‘…

《OEA - 实体扩展属性系统 - 设计方案说明书》

这篇设计文档是 12 月份写来参加公司的研发峰会的,自己倒是信心满满,不过最后还是没有入围。现在想想也没啥大用,所以贴出来,期待与园友交流。 文档有点长,没全部贴在博客中,有兴趣的可以下载附件中的 PDF。…

Amazon S3 设置对象的生命周期Lifecycle

2019独角兽企业重金招聘Python工程师标准>>> 保存在S3上面的文件,有些是长期保存的,而有一些我们却不希望保存太久,过一段时间后就可以删除了。例如备份文件,太旧的就要删除;又或如日志文件,只保…

Direct3D提高篇:HLSL编程实现PhotoShop滤镜效果 - 伪 HDR/Blow

所谓HDR就是高动态范围的意思。在普通的显示器和位图里,每通道都是8-bit,也就是说RGB分量的范围都是0-255,这用来表示现实中的颜色显然是远远不够的。现实中的图像的动态范围远远大的多,那么如何在现有的显示设备里尽可能的保持更…

Oracle Buffer Cache的keep、recycle、default pool概念图解

Oracle Buffer Cache的keep、recycle、default pool概念图解 转载于:https://blog.51cto.com/maclean/1278284

程序中调用命令行命令,不显示那个黑黑的DOS窗口

From: http://hi.baidu.com/yanqiuge/item/5da466c3d3f18453ad00ef1d 在程序中使用命令行时,会有黑黑的DOS窗口,很不爽,在网上查了一下,写一个有点实用价值的小程序: /****************************** 文件名&#…

Oracle Row cache lock图解

Oracle Row cache lock(dictionary cache)图解 转载于:https://blog.51cto.com/maclean/1278273

windows平台编译vlc

From: http://blog.csdn.net/guanshangming/article/details/5581270 转自:http://jeremiah.blog.51cto.com/539865/114190Jeremiah刚刚工作几个月,参与的第一个项目是与视频监控有关,分配给我的任务就是用开源的vlc做一个自己的播放器。对于开源项目来…

Appium--环境搭建

Appium介绍 Appium是一个移动端的自动化框架,可以做H5、Web测试、可以做功能测试(Android、IOS)、可以做跨进程测试,且是跨平台的。Appium做测试的时候支持的语言有很多种,包括java、Python、ruby、PHP、C#等。由于我熟…

Windows 系统下Git安装图解

From: http://blog.csdn.net/jiguanghoverli/article/details/7902791 Windows 系统下Git安装图解 简单来说Git是一个免费的、开源的版本控制软件,从功能上讲,跟我们比较熟悉的Subversion(SVN)这类版本控制软件没什么两样。由于工作的需求,需…

【fedora12】vlc-2.0.0编译

From: http://www.zhouwenyi.com/node/1348 1. 官网下载 VLC 源码 (vlc- 2.0.0.tar.xz) 下载地址为: http://download.videolan.org/pub/videolan/vlc/2.0.0/vlc-2.0.0.tar.xz 2. 解压VLC源码: xz -dvlc-2.0.0.tar.xz tar -xvfvlc-2.0.0.tar 3. cdvlc-2.0.0 …

Android实现3D旋转效果

下面的示例是在Android中实现图片3D旋转的效果。实现3D效果一般使用OpenGL,但在Android平台下可以不直接使用OpenGL,而是使用Camera实现,Camera中原理最终还是使用OpenGL,不过使用Camera比较方便。 Camera类似一个摄像机&#xff…

搭建hadoop开发环境--基于xp+cygwin

2019独角兽企业重金招聘Python工程师标准>>> 1.安装cygwin 参考博文:http://hi.baidu.com/%BD%AB%D6%AE%B7%E7_%BE%B2%D6%AE%D4%A8/blog/item/8832551c7598551f314e15c2.html Q1.实际安装中在第9步 “打开cygwin进行配置,首先输入&#xff…

random---伪随机数生成器

python3.5 作用:实现了多种类型的伪随机数生成器 random模块基于Mersenne Twister 算法提供了一个快速伪随机数生成器。原先开发这个生成器是为了向蒙特卡洛模拟生成输入,Mersenne Twister算法会生成有一个大周期的近均匀分布的数,以适用于各…

新浪微博iOS版SDK“宝玉XP”框架学习笔记

本文为论坛会员3h2om分享,对新浪微博iOS版SDK-“宝玉XP”框架进行研究所写的学习笔记,非常详细和精彩。 本人刚入学iOS开发,在学习的过程中,对新浪微博iOS版SDK-“宝玉XP”框架进行了学习(下载地址:https:/…

Lync Server 2010所需媒体网络流量带宽详解和计算

如果在组织内部部署Lync Server 2010,那么最大的优势就是解决了组织内部的即时通讯需求,为什么这么说?因为纵观现在微软所推行的商业智能应用平台,可以非常直观的了解到实际上整个微软商业平台就是由Lync Server、Exchange Server…

Linux互斥锁的使用代码实现

From: http://blog.csdn.net/leo115/article/details/8037869 [cpp] view plaincopy#include <stdio.h> #include <pthread.h> #include <sched.h> #include <unistd.h> //对临界区的保护问题 void *fun1(void *arg); void *fun2(void *ar…