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,一经查实,立即删除!

相关文章

SAFEARRAY使用实例

From: http://blog.csdn.net/csfreebird/article/details/234547 目录:SAFEARRAY使用实例目录:前言:何谓SAFEARRAY:创建SAFEARRAY:方法一:使用SafeArrayAllocDescriptor在栈上创建一维数组方法二:使用Safe…

前端第一天 HTML基础

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

把一个结构体当做属性后碰到的问题

当我把一个"结构体"在类中当做属性后, 在实用中可以直接读取结构体成员, 但不能直接写入...下面是由此引发的小练习:unit Unit1;interfaceusesWinapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,Vcl.Controls, Vcl.…

VC6 + OpenCV1.0实现图片缩放显示

用vc6新建一个win32控制台程序,代码: /*功能:实现加载jpg图片,并进行缩放显示开发环境: winXP vc6 openCV1.0头文件路径:D:\opensource\opencv1.0\cv\includeD:\opensource\opencv1.0\cxcore\includeD:\opensource\opencv1.0\ot…

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

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

[react] react中你用过哪些第三方的中间件

[react] react中你用过哪些第三方的中间件 redux-thunk: Redux的异步处理方案,actionCreator中可以返回一个函数(即可以dispatch一个function),函数内在写异步的代码redux-saga: Redux的异步处理方案,没有破坏redux中dispatch一个…

从PeopleEditor控件中取出多用户并更新到列表

如果一个列表中有一个字段类型为用户或用户组,并且设置为用户,允许多值的话,那么用代码进行更新的时候就必须将这个字段的值赋成SPFieldUserValueCollection类型,以下代码即为从PeopleEditor控件中取出多个用户并返回一个SPFieldU…

[react] 怎样在react中使用innerHTML?

[react] 怎样在react中使用innerHTML? 使用dangerouslySetInnerHTML属性,该属性传入一个对象,对象中__html属性的值即时innerHTML的富文本代码 个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。…

vc++出现warningC4819的处理方法

From: http://blog.sina.com.cn/s/blog_93e339ba01014fiw.html 编译VC程序的时候出现如下提示警告: warning C4819: The file contains a character that cannot berepresented in the current code page (936). Save the file inUnicode format to prevent data l…

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 …

[react] 请说说什么是useState?为什么要使用useState?

[react] 请说说什么是useState?为什么要使用useState? useState是React原生的Hook,它接受一个参数,这个参数可以是对象或者普通的基本数据类型的值,也可以是一个有返回值的函数,useState函数返回一个数组&…

vc2010+openCV1.0实现将指定目录下的所有jpg文件缩放后存放到目标文件夹

开发环境:winXP vc2010 OpenCV1.0 OpenCV1.0安装目录: D:\opensource\opencv1.0 源代码: /*功能:将指定目录下的所有JPG文件进行缩放后存放到目标文件夹开发环境: winXP vc2010 openCV1.0头文件路径:D:\opensource\opencv1.…

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

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

[react] react中的setState缺点是什么呢?

[react] react中的setState缺点是什么呢? 调用时机不恰当的话可能引起循环调用的问题:比如在componentWillUpdate render componentDidUpdate调用都有可能引起这种问题setState可能会引用不必要的re-render:setState任何值都会引起组件的ren…

一个系统中同时使用VC6.0+OpenCV1.0和VS2010+OpenCV2.4.6.0的方法

From: http://blog.csdn.net/zzy7222872/article/details/6047446 以前用的是VC6.0OpenCV1.0的组合,一直用的很好。一般的图像处理算法都可以实现,现在突然想搞一下立体视觉方面的东西,查看了OpenCV的手册,发现立体视觉上的大部…

[react] 请说说什么是useEffect?

[react] 请说说什么是useEffect? useEffect是副作用函数,第一个参数是函数,第二个参数是依赖的数据数组,当依赖数组中的数据变化时,触发第一个参数函数的执行。有以下的几种使用方式 模拟componentDidMount&#xff…

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

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

Python与C/C++ 模块相互调用

From: http://hi.baidu.com/jintuguo/item/45639b4e7cda3c9f833ae1bb Python调用C动态链接库 Python调用C库很简单,不经过任何封装打包成so,再使用python的ctypes调用即可。 <test.cpp 生成动态库的源文件> #include <stdio.h> extern "C" { void…

[react] 在构造函数中调用super(props)的目的是什么?

[react] 在构造函数中调用super(props)的目的是什么&#xff1f; 这是ES6的语法。class组件继承自React.Component&#xff0c;super(props)之后&#xff0c;有以下几个作用&#xff1a; 初始化props&#xff0c;虽然不进行super(props)操作&#xff0c;组件在实例化时react也…

coverage.py - python 单元测试覆盖率统计工具

前提&#xff1a;1.假定已经安装好coverage.py&#xff08;ubuntu 10.10python.2.7coverage3.5.1&#xff09;2.项目里有模块do.py以及测试它的单元测试模块doTEST.py命令行&#xff1a;$ cd /home/user1/workspace/hp1$ coverage run doTEST.py$ coverage report$ coverage ht…