参加一个.NET培训后的若干笔记

1. 上午老师提到一个thread必须隶属于某个appDomain,我觉得有点疑惑。简单在网上找了找,下面的关于soft thread和hard thread的阐述是我比较信服的:
AppDomain是个静态概念,只是限定了对象的边界;线程是个动态概念,它可以运行在不同的AppDomain
一个AppDomain内可以创建多个线程,但是不能限定这些线程只能在本AppDomain内执行代码。

CLR中的System.Threading.Thread对象其实是个soft thread,它并不能被操作系统识别;操作系统能识别的是hard thread
一个soft thread只属于一个AppDomain,穿越AppDomain的是hard thread。当hard thread访问到某个AppDomain,一个AppDomain就会为之产生一个soft thread

hard threadthread local storage(TLS),这个存储区被CLR用来存储这个hard thread当前对应的AppDomain引用以及soft

thread引用。当一个hard thread穿越到另外一个AppDomain时,TLS中的这些引用也会改变。

 

2.关于程序集的“热插拔”

杨老师提到过保持程序集对外接口稳定可以实现程序集的“热插拔”(即在不停止主应用程序的前提下,动态替换单个程序集)。下来查了一下,这种替换应该是 有很多条件的,因为.net不支持对单个程序集的Unload()操作,你必须把要替换的程序集放置在一个单独的AppDomain中运行,替换程序集的时候要杀掉这个AppDomain,在一个新的AppDomainLoad新版本的程序集。

(AssemblyLoader.RemoteLoader)domain.CreateInstanceFromAndUnwrap(
                "AssemblyLoader.dll","AssemblyLoader.RemoteLoader"); 

所以,对这个程序集中方法的调用很多都将是跨边界的远程调用,性能上应该很成问题吧?有没有哪位同志以前在项目中这样用过?

 

3.哈希/字典的底层结构是什么?其实就是数组,而根据key利用散列算法计算出来的hashcode自然成为了数组的下标。
Hashtable用来存放数据的是bucket数组

[StructLayout(LayoutKind.Sequential)]
private struct bucket
{
    
public object key;
    
public object val;
    
public int hash_coll;
}

而Dictionary<TKey,TValue>用来存放数据的是Entry数组(里面多了一项next,同学们可以自己想想他用来做什么^_^)

[StructLayout(LayoutKind.Sequential)]
private struct Entry
{
    
public int hashCode;
    
public int next;
    
public TKey key;
    
public TValue value;
}

既然是数组,那么这个数组应该申请多大长度呢?即便你在构造函数中进行了指定,.net也不会完全以你指定的为准,而是会从素数表里面找一个素数:

private void Initialize(int capacity)
{
    
int prime = HashHelpers.GetPrime(capacity);
    
this.buckets = new int[prime];
    
for (int i = 0; i < this.buckets.Length; i++)
    {
        
this.buckets[i] = -1;
    }
    
this.entries = new Entry<TKey, TValue>[prime];
    
this.freeList = -1;
}

那么如果你插入的数据量很大,预先申请的数组不够用了怎么办?扩容。
扩容是个耗时非常惊人的内部操作,Hashtable 之所以写入效率仅为读取效率的 1/10 数量级, 频繁的扩容是一个因素。当进行扩容时,散列表内部要重新 new 一个更大的数组,然后把原来数组的内容拷贝到新数组,并进行重新散列。如何 new 这个更大的数组也有讲究。散列表的初始容量一般来讲是个素数。当扩容时,新数组的大小会设置成原数组双倍大小的相近的一个素数。为了避免生成素数的额外开销,.NET 内部有一个素数数组,记录了常用到的素数。如下所示:
static HashHelpers()
{
    primes 
= new int[] { 
        
37110x110x170x1d0x250x2f0x3b0x470x590x6b0x830xa30xc50xef
        
0x1250x1610x1af0x2090x2770x2f90x3970x44f0x52f0x63d0x78b0x91d0xaf10xd2b0xfd10x12fd
        
0x16cf0x1b650x20e30x27770x2f6f0x38ff0x446f0x521f0x628d0x76550x8e010xaa6b0xcc890xf5830x126a70x1619b
        
0x1a8570x1fd3b0x263150x2dd670x3701b0x420230x4f3610x5f0ed0x721250x88e310xa443b0xc51eb0xec8c10x11bdbf0x154a3f0x198c4f
        
0x1ea8670x24ca190x2c25c10x34fa1b0x3f928f0x4c49870x5b8b6f0x6dda89
     };
}


4. Delegate的序列化

今天杨老师还谈到了代理序列化的问题。我们都知道代理后面可以挂一个很长的委托链,那序列化的时候这个委托链怎么办?google了一下,答案竟然是整个委托链都要被序列化,这就要求委托链上的每个对象都是可序列化的(要求很苛刻阿)。所以,delegate的序列化要慎用。对了,还查到一个有趣的结果,以前没注意到:

delegate 开头是小写的 d,如果换成大写开头的 Delegate,那差别可就大了,简直是孙子和爷爷的区别。为什么说是孙子和爷爷呢?因为 delegate 仅仅是 C# 的关键字,表示一个继承自 System.MulticastDelegate 的具体委托类,而 Delegate 却是  System.MulticastDelegate 的父类,这还不是孙子和爷爷吗。另外,Delegate  System.MulticastDelegate 都是抽象类,只有编译器才可以从此类派生。也就是说,除了用 delegate 这种形式,我们不能显式地从这两个类派生。”

posted @ 2009-08-07 16:59  EagleFish(邢瑜琨)  阅读(1659)  评论(0编辑  收藏  举报