贝网博客

我的分类
流水记事
源码下载
Asp.net
其它
数据库
Javascript
.Net技术
我的相册
友情链接
博客园
CSDN博客
Start0
最新回复
fasdfasdf
[:..
lz这个东西好厉害,我..
哈哈,好照片
不错,以前一直用黄色..
终于找到支持ff的修正..
终于找到支持ff的修正..
新鲜性
看看,试试,好不好使。
好东西一起学习[:img0..
.Net技术 日志列表    
本站一共有博客83条,当前显示26条
 发表于:2012-08-10 15:21:54
更新于:2012-10-29 16:32:39

1、使用C#封装好的方法截屏:
Size sz = Screen.PrimaryScreen.Bounds.Size;
int width = sz.Width + 300;// 加300是为了测试双显示器时能否正常截屏
int height = sz.Height;
using (Bitmap bit = new Bitmap(width, height))
using (Graphics g = Graphics.FromImage(bit))
{
    g.CopyFromScreen(this.Location, new Point(0, 0), bit.Size);
    bit.Save(@"c:\b.png");
    g.Dispose();
}

2、调用系统API进行截屏
先定义外部方法:
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int
wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);

然后截屏代码如下:
Size sz = Screen.PrimaryScreen.Bounds.Size;
int width = sz.Width + 300; // 加300是为了测试双显示器时能否正常截屏
int height = sz.Height;

//调用API截屏
IntPtr hDesk = GetDesktopWindow();
IntPtr hSrce = GetWindowDC(hDesk);
IntPtr hDest = CreateCompatibleDC(hSrce);
IntPtr hBmp = CreateCompatibleBitmap(hSrce, width, height);
IntPtr hOldBmp = SelectObject(hDest, hBmp);
if (BitBlt(hDest, 0, 0, width, height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt))
{
    using (Bitmap bmp = Image.FromHbitmap(hBmp))
    {
        SelectObject(hDest, hOldBmp);
        DeleteObject(hBmp);
        DeleteDC(hDest);
        ReleaseDC(hDesk, hSrce);
        bmp.Save(@"c:\a.png");
        bmp.Dispose();
    }
}

 发表于:2012-05-03 11:30:25
更新于:2012-07-27 11:00:26

这几天开发了一个Web站点,主要用于给其它站点做接口数据返回,在提交压力测试时,经常内存增长到1G后,溢出了,因为程序本身确实会加载不少缓存数据,而服务器本身是Windows2003 32位的操作系统,最大只支持2g内存,通过参考页面:http://support.microsoft.com/?kbID=810371,在boot.ini里增加启动参数:/3gb /Userva=3030
从而让程序可以使用到3G内存,然后继续提交压力测试

结果测试时,内存依旧持续增长,增长到2G后,又内存溢出了,至此,怀疑程序有内存泄露

找了一个win7版本的任务管理器,拷贝到服务器上,在内存达到2G时,创建了一个w3wp进程的dump(下面的创建转储文件)

抓下来dump文件后,在本地用Windbg加载sos.dll进行分析,
.load C:\WINDOWS\microsoft.net\framework\v2.0.50727\sos.dll

通过!dumpheap -stat命令,发现占用内存最大的3个对象是:
6611b1b0  5027584    241324032 System.Web.SessionState.InProcSessionState
66149064  5027602    361987344 System.Web.Caching.CacheEntry
79330b24  5575562    378271928 System.String

各占用了241M、361M、378M,因为事先知道String占用这么大是正常的,所以要去分析另外2个对象,在Windbg里通过:  !dumpheap -type System.Web.Caching.CacheEntry  命令,查看所有具体的CacheEntry地址等信息,这个会很多,数据一出来,马上按Ctrl+Break,数据大致如下
 0:000> !dumpheap -type System.Web.Caching.CacheEntry
------------------------------
Heap 0
 Address       MT     Size
106837a8 044b3ac4       72    
107b0de0 044b3ac4       72    
107b1e74 044b3ac4       72    
107bdc14 044b3ac4       72    
107bdef8 044b3ac4       72    
107c1290 044b3ac4       72    
107c1370 044b3ac4       72    
107c142c 044b3ac4       72    
107c20f8 044b3ac4       72    

先找第一个地址分析:
0:000> !do 106837a8
Name: System.Web.Caching.CacheEntry
MethodTable: 044b3ac4
EEClass: 044aac24
Size: 72(0x48) bytes
 (C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
02469134  400140c        4        System.String  0 instance 10683740 _key
030fb350  400140d        c          System.Byte  1 instance       16 _bits
03154d18  400140e        8         System.Int32  1 instance -1350406618 _hashCode
02466d54  4001415       10        System.Object  0 instance 10683794 _value
0321ab48  4001416       1c      System.DateTime  1 instance 106837c4 _utcCreated
0321ab48  4001417       24      System.DateTime  1 instance 106837cc _utcExpires
0321dda4  4001418       2c      System.TimeSpan  1 instance 106837d4 _slidingExpiration
030fb350  4001419        d          System.Byte  1 instance 4294967295 _expiresBucket
044b3964  400141a       34 ...g.ExpiresEntryRef  1 instance 106837dc _expiresEntryRef
030fb350  400141b        e          System.Byte  1 instance        2 _usageBucket
044b3a6c  400141c       38 ...ing.UsageEntryRef  1 instance 106837e0 _usageEntryRef
0321ab48  400141d       3c      System.DateTime  1 instance 106837e4 _utcLastUpdate
044b32f0  400141e       14 ...g.CacheDependency  0 instance 00000000 _dependency
02466d54  400141f       18        System.Object  0 instance 00000000 _onRemovedTargets
0321ab48  4001412      1e4      System.DateTime  1   shared   static NoAbsoluteExpiration
    >> Domain:Value  000dd050:NotInit  0012ad78:1616ba94 <<
0321dda4  4001413      1e8      System.TimeSpan  1   shared   static NoSlidingExpiration
    >> Domain:Value  000dd050:NotInit  0012ad78:1616baa4 <<
0321dda4  4001414      1ec      System.TimeSpan  1   shared   static OneYear
    >> Domain:Value  000dd050:NotInit  0012ad78:1616bab4 <<

然后再看_key的值
0:000> !do 10683740
Name: System.String
MethodTable: 02469134
EEClass: 02a42780
Size: 82(0x52) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: f2029612887/recommendservice.svc

再看_value的值
0:000> !do 10683794
Name: System.Web.Configuration.MapPathCacheInfo
MethodTable: 047026d0
EEClass: 04776bb8
Size: 20(0x14) bytes
 (C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
02469134  40018d4        4        System.String  0 instance 10683dfc MapPathResult
0320bdb8  40018d5        c       System.Boolean  1 instance        1 Evaluated
030f0498  40018d6        8     System.Exception  0 instance 00000000 CachedException
0:000> !do 10683dfc
Name: System.String
MethodTable: 02469134
EEClass: 02a42780
Size: 88(0x58) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: E:\wwwroot\Web\recommendservice.svc
看来第一个CacheEntry保存的是一个虚拟路径和物理路径的对应关系,接着分析下一个CacheEntry的数据
如此类推发现
!do 107b0de0得到的也是一个文件路径缓存数据
!do 107b1e74得到System.Web.Security.FileSecurityDescriptorWrapper,看起来像是文件安全配置说明
!do 107bdc14得到一个key为jbj1dejrlaibnmfyrnqldwk45的System.Web.SessionState.InProcSessionState对象
!do 107bdef8得到一个key为jfd2qmyinc5sgpk55b2nks4rw的System.Web.SessionState.InProcSessionState对象
107c1290、107c1370、107c142c、107c20f8等大部分CacheEntry保存的都是InProcSessionState对象

由此可见,占用内存第二大的,都引用了占用内存第三大的InProcSessionState对象,
而Asp.net的Session默认是保持20分钟,压力测试时,在20分钟内创建的Session全部被保持,无法被GC回收,从而内存不断增长,最终导致内存溢出了

马上咨询压力测试人员,果然是压力测试发起了所有连接,都是新连接,
通过修改压力测试方案,复用创建的连接,而不是发起 新连接,果然内存占用在400M左右,增长也极其缓慢

最后的修改方案,因为此站点仅作为接口使用,不会使用到Session,直接在Web.config增加配置:<sessionState mode="Off"></sessionState>
关闭Session,再进行压力测试,内存基本不会增长

 发表于:2012-07-12 13:15:49

参考:http://technet.microsoft.com/zh-cn/library/cc737705(v=ws.10)
下面是我的简单翻译内容:

开启“保持HTTP连接”以保持连接开启
一个浏览器通常会发起多个请求以下载整个web页。为了提升服务器性能,大多数浏览器都会要求服务器在这多个请求之间保持连接状态,这个我就是Http协议里的“HTTP Keep-Alives”。

如果没有“HTTP Keep-Alives”,浏览器为页面里的多个对象发起多个web请求,比如页面里的图片,需要为这每一个图片发起一个独立的连接请求。这些附加的请求和连接,都需要额外的服务器响应和资源分配,大大降低了服务器的效率。这些附加的连接请求也会导致浏览器变的更慢或卡死(失去响应),尤其是通过网速慢的情况下。
“HTTP Keep-Alives”在IIS6.0里默认是开启的,这符合HTTP/1.1的规范要求。IIS会保持一个不活动的连接为开启状态,直到这个连接时间超过设置的值(这个默认值是120秒)。
如果你禁用“HTTP Keep-Alives”,服务器会忽略客户端浏览器保持连接开启的请求。因此,只有在特殊情况下才去禁用“HTTP Keep-Alives”,并且你必须完全清楚的明白这个会对你的服务器造成什么样的影响。
“HTTP Keep-Alives”需要集成安全或基于连接的身份验证服务,比如Windows的集成身份验证。对于使用Windows集成身份验证的站点,如果你禁用“HTTP Keep-Alives”,那么对这个站点的请求都会失败。
要验证你的服务器是不是启用了“HTTP Keep-Alives”,或要去开启“HTTP Keep-Alives”,请参考:
http://technet.microsoft.com/zh-cn/library/cc780771(v=ws.10)

回复:7  发表于:2010-11-23 16:55:24
更新于:2012-06-07 11:30:45

重新书写一下结论,代码中,如果经常使用List的Contains方法,建议修改为使用Dictionary的ContainsKey来代替,也可以使用下文中的SortedList,但是如果有比较多的插入操作,则不建议使用SortedList,而还是使用Dictionary,因为SortedList是线性存储,在修改数据时效率很低;
如果需要数组进行排序,可以改用SortedDictionary,这个的效率,我的测试结果比Dictionary差一点点,可以忽略,但是它是链表存储,占用内存会比SortedList大

本文介绍了C#中的List与SortedList的Contains方法源码,并进行了效率比较,得出结果是SortedList的Contains效率较高,但是插入数据时效率较低

做了个项目,是抽取不重复的随机数,一开始的代码(删减版)是:
static List<int> ok = new List<int>();
public static int GetRandom(){
    int ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, 10000);
    while (ok.Contains(ret))
    {
        ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, 1000);
    }
    ok.Add(ret);
    return ret;
}

后来觉得数组大了后,效率较低,研究了一下List<T>.Contains方法,定义如下:
for(int i=0; i<_size; i++) {
    if (c.Equals(_items[i], item)) return true;
}
return false;
就是循环整个数组去查找,后来想了一下,如果用排序好的数组,会不会效率更高呢?
又研究了一下SortedList<T>.ContainsKey方法,它调用了Array.BinarySearch方法,BinarySearch方法MSDN说明:
    使用指定的 IComparer<(Of <(T>)>) 泛型接口,在一维排序 Array 的某个元素范围中搜索值。定义大致如下:
private static int BinarySearch(T[] array, int index, int length, T value)
{
  int num = index;
  int num2 = (index + length) - 1;
  while (num <= num2)
  {
    int num4;
    int num3 = num + ((num2 - num) >> 1);
    num4 = array[num3].CompareTo(value);
    if (num4 == 0)
    {
      return num3;
    }
    if (num4 < 0)
    {
      num = num3 + 1;
    }
    else
    {
      num2 = num3 - 1;
    }
  }
  return ~num;
}
可以看出来是使用二分法查找,只是在Add的时候也要找到相应元素位置去Insert

通过几次大数据量的测试,在List.Add List.Contains 和 SortedList.Add SortedList.ContainsKey方法对比中,后者胜出,于是我的代码就改成了:
static SortedList<int, int> ok = new SortedList<int, int>();
public static int GetRandom(){
    int ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, 1000);
    while (ok.ContainsKey(ret))
    {
        ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, 1000);
    }
    ok.Add(ret, 1);
    return ret;
}

今天发现还有个SortedDictionary,汗,这个玩意的效率更高,测试代码如下,在下面的代码中SortedDictionary的时间都没超过100ms:
static void Main(string[] args)
{
    int time = 10000;
    Stopwatch w = new Stopwatch();
    for (var j = 0; j < 20; j++)
    {
        w.Reset();
        w.Start();
        for (int i = 0; i < time; i++)
            GetRandom2();
        w.Stop();
        Console.WriteLine("SortedDictionary耗时" + w.ElapsedMilliseconds);
        w.Reset();
        w.Start();
        for (int i = 0; i < time; i++)
            GetRandom();
        w.Stop();
        Console.WriteLine("SortedList耗时" + w.ElapsedMilliseconds);
        w.Reset();
        w.Start();
        for (int i = 0; i < time; i++)
            GetRandom3();
        w.Stop();
        Console.WriteLine("List耗时" + w.ElapsedMilliseconds);
    }
    Console.ReadLine();
}

private static int rndMax = int.MaxValue;
static SortedList<int, int> ok = new SortedList<int, int>();
public static void GetRandom()
{
    int ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, rndMax);
    while (ok.ContainsKey(ret))
        ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, rndMax);
    ok.Add(ret, 1);
}

static SortedDictionary<int, int> ok2 = new SortedDictionary<int, int>();
public static void GetRandom2()
{
    int ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, rndMax);
    while (ok2.ContainsKey(ret))
        ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, rndMax);
    ok2.Add(ret, 1);
}
static List<int> ok3 = new List<int>();
public static void GetRandom3()
{
    int ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, rndMax);
    while (ok3.Contains(ret))
        ret = new Random(Guid.NewGuid().GetHashCode()).Next(1, rndMax);
    ok3.Add(ret);
}

回复:1  发表于:2012-03-13 17:45:13
更新于:2012-06-07 11:26:53

一直在使用WebForm,最近领导要求项目开始使用MVC,因为服务器还没有安装Framework4,先从MVC2开始吧

1、在IIS里访问MVC路由时,出现404错误,但是在Visual Studio开发服务器里正常
这是因为没有配置IIS通配符应用程序映射,开始=>运行,输入Inetmgr,进入Internet信息服务管理器,在你的网站或虚拟目录上按右键=>属性=>主目录,点击下面的“配置”,在下方的“通配符应用程序映射”里,点“插入”,可执行文件里输入“c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll”,并去掉“确认文件是否存在”,最后点确定即可,现在IIS里访问也正常了
这里有点问题,按上面配置后,任意扩展名请求,都会交给aspnet_isapi.dll去处理了,包括html、jpg、js等常见扩展名了
即使通过routes.IgnoreRoute("{*dir}", new { dir = @".*\.html(.*)?" });忽略这些常用扩展名,也会进isapi走一趟,目前我的做法就是MVC都加一个ashx扩展名,这样IIS也不用配置了(Application_BeginRequest里加断点,可以看到所有的html等请求都会到这个断点)

我用的路由:routes.MapRoute("Default","{controller}.ashx/{action}/{*path}",new { controller = "Home", action = "Index" });

2、路由里的参数名称必须与Controller里的参数名称一致,如配置了一个路由:
routes.MapRoute("mytest","{controller}/MyTest/{id}",new { controller = "Home", action = "MyTest" });
那么HomeController里的方法必须为:
public ActionResult MyTest(string id){}// 如果参数名不是id,而是abc等其它名,就会出现找不到对应的Controller的错误
简单的作法就是配置路由时,不指定参数

3、参数约束:
routes.MapRoute("mytest","{controller}/MyTest/{id}",new { controller = "Home", action = "MyTest" },
new {id = @"\d*"} );// 这个约束表明id必须为数字,而且必须全部是数字(有点疑惑,全数字的正则应该是^\d+$,这里没有开始结束符也可以做到)

4、执行前方法OnActionExecuting:在这里可以在Action执行前做任意操作,比如登录检查等等,如:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    string controllName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
    //string actionName = filterContext.ActionDescriptor.ActionName;
    if (!controllName.Equals("Account", StringComparison.OrdinalIgnoreCase)) {
        if (!IsLogin)  {
            filterContext.Result = RedirectToAction("LogOn", "Account"); // Redirect("/Account.ashx/LogOn");
            return;
        }
    }
            
    base.OnActionExecuting(filterContext);
}

5、Controller里的方法不允许重载,即不允许有2个方法名字一样,会造成访问时出错,不知道调用哪个方法

6、通过Post提交数据时,可以通过 方法参数名1=值&方法参数名2=值&……
以便直接把值绑定到参数里(不需要通过Request.Form去自己匹配,MVC自己搞定了),对于Class类型,可以提交json格式,MVC自己转换为这个类型的参数

7、正常情况下,POST数据类型都是:Content-Type: application/x-www-form-urlencoded; charset=UTF-8
某些客户端POST数据时,可能提交类型为:Content-Type: application/octet-stream,这会导致MVC认不到Request.Form,此时需要自行去处理POST参数,如代码:
public ActionResult MVC1(string para)
{
    if (para == null)
    {
        // 当客户端用Content-Type: application/octet-stream传递时
        if (Request.InputStream != null && Request.InputStream.Length > 0)
        {
            string postData;
            using (Stream inStream = Request.InputStream)
            using (StreamReader reader = new StreamReader(inStream, Encoding.UTF8))
            {
                postData = reader.ReadToEnd();
            }
            NameValueCollection tmpNV = HttpUtility.ParseQueryString(postData);
            if(tmpNV != null)
            {
                para = tmpNV["para"];
            }
        }
    }

 发表于:2012-03-21 09:42:15
更新于:2012-06-07 09:38:53

我们可能经常会用到 Thread.Sleep 函数来使线程挂起一段时间。那么你有没有正确的理解这个函数的用法呢?思考下面这两个问题:

  1. 假设现在是 2008-4-7 12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在 2008-4-7 12:00:01.000 的时候,这个线程会 不会被唤醒?
  2. 某人的代码中用了一句看似莫明其妙的话:Thread.Sleep(0) 。既然是 Sleep 0 毫秒,那么他跟去掉这句代码相比,有啥区别么?

我们先回顾一下操作系统原理。

操作系统中,CPU竞争有很多种策略。Unix系统使用的是时间片算法,而Windows则属于抢占式的。

在时间片算法中,所有的进程排成一个队列。操作系统按照他们的顺序,给每个进程分配一段时间,即该进程允许运行的时间。如果在 时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程 序所要做的就是维护一张就绪进程列表,,当进程用完它的时间片后,它被移到队列的末尾。

所谓抢占式操作系统,就是说如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU 。因此可以看出,在抢 占式操作系统中,操作系统假设所有的进程都是“人品很好”的,会主动退出 CPU 。

在抢占式操作系统中,假设有若干进程,操作系统会根据他们的优先级、饥饿时间(已经多长时间没有使用过 CPU 了),给他们算出一 个总的优先级来。操作系统就会把 CPU 交给总优先级最高的这个进程。当进程执行完毕或者自己主动挂起后,操作系统就会重新计算一 次所有进程的总优先级,然后再挑一个优先级最高的把 CPU 控制权交给他。

我们用分蛋糕的场景来描述这两种算法。假设有源源不断的蛋糕(源源不断的时间),一副刀叉(一个CPU),10个等待吃蛋糕的人(10 个进程)。

如果是 Unix操作系统来负责分蛋糕,那么他会这样定规矩:每个人上来吃 1 分钟,时间到了换下一个。最后一个人吃完了就再从头开始。于是,不管这10个人是不是优先级不同、饥饿程度不同、饭量不同,每个人上来的时候都可以吃 1 分钟。当然,如果有人本来不太饿,或者饭量小,吃了30秒钟之后就吃饱了,那么他可以跟操作系统说:我已经吃饱了(挂起)。于是操作系统就会让下一个人接着来。

如果是 Windows 操作系统来负责分蛋糕的,那么场面就很有意思了。他会这样定规矩:我会根据你们的优先级、饥饿程度去给你们每个人计算一个优先级。优先级最高的那个人,可以上来吃蛋糕——吃到你不想吃为止。等这个人吃完了,我再重新根据优先级、饥饿程度来计算每个人的优先级,然后再分给优先级最高的那个人。

这样看来,这个场面就有意思了——可能有些人是PPMM,因此具有高优先级,于是她就可以经常来吃蛋糕。可能另外一个人是个丑男,而去很ws,所以优先级特别低,于是好半天了才轮到他一次(因为随着时间的推移,他会越来越饥饿,因此算出来的总优先级就会越来越高,因此总有一天会轮到他的)。而且,如果一不小心让一个大胖子得到了刀叉,因为他饭量大,可能他会霸占着蛋糕连续吃很久很久,导致旁边的人在那里咽口水。。。
而且,还可能会有这种情况出现:操作系统现在计算出来的结果,5号PPMM总优先级最高,而且高出别人一大截。因此就叫5号来吃蛋糕。5号吃了一小会儿,觉得没那么饿了,于是说“我不吃了”(挂起)。因此操作系统就会重新计算所有人的优先级。因为5号刚刚吃过,因此她的饥饿程度变小了,于是总优先级变小了;而其他人因为多等了一会儿,饥饿程度都变大了,所以总优先级也变大了。不过这时候仍然有可能5号的优先级比别的都高,只不过现在只比其他的高一点点——但她仍然是总优先级最高的啊。因此操作系统就会说:5号mm上来吃蛋糕……(5号mm心里郁闷,这不刚吃过嘛……人家要减肥……谁叫你长那么漂亮,获得了那么高的优先级)。

那么,Thread.Sleep 函数是干吗的呢?还用刚才的分蛋糕的场景来描述。上面的场景里面,5号MM在吃了一次蛋糕之后,觉得已经有8分饱了,她觉得在未来的半个小时之内都不想再来吃蛋糕了,那么她就会跟操作系统说:在未来的半个小时之内不要再叫我上来吃蛋糕了。这样,操作系统在随后的半个小时里面重新计算所有人总优先级的时候,就会忽略5号mm。Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。

看完了 Thread.Sleep 的作用,我们再来想想文章开头的两个问题。

对于第一个问题,答案是:不一定。因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。

与此相似的,Thread有个Resume函数,是用来唤醒挂起的线程的。好像上面所说的一样,这个函数只是“告诉操作系统我从现在起开始参与CPU竞争了”,这个函数的调用并不能马上使得这个线程获得CPU控制权。

对于第二个问题,答案是:有,而且区别很明显。假设我们刚才的分蛋糕场景里面,有另外一个PPMM 7号,她的优先级也非常非常高(因为非常非常漂亮),所以操作系统总是会叫道她来吃蛋糕。而且,7号也非常喜欢吃蛋糕,而且饭量也很大。不过,7号人品很好,她很善良,她没吃几口就会想:如果现在有别人比我更需要吃蛋糕,那么我就让给他。因此,她可以每吃几口就跟操作系统说:我们来重新计算一下所有人的总优先级吧。不过,操作系统不接受这个建议——因为操作系统不提供这个接口。于是7号mm就换了个说法:“在未来的0毫秒之内不要再叫我上来吃蛋糕了”。这个指令操作系统是接受的,于是此时操作系统就会重新计算大家的总优先级——注意这个时候是连7号一起计算的,因为“0毫秒已经过去了”嘛。因此如果没有比7号更需要吃蛋糕的人出现,那么下一次7号还是会被叫上来吃蛋糕。

因此,Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.Sleep(0) ,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。

末了说明一下,虽然上面提到说“除非它自己放弃使用 CPU ,否则将完全霸占 CPU”,但这个行为仍然是受到制约的——操作系统会监控你霸占CPU的情况,如果发现某个线程长时间霸占CPU,会强制使这个线程挂起,因此在实际上不会出现“一个线程一直霸占着 CPU 不放”的情况。至于我们的大循环造成程序假死,并不是因为这个线程一直在霸占着CPU。实际上在这段时间操作系统已经进行过多次CPU竞争了,只不过其他线程在获得CPU控制权之后很短时间内马上就退出了,于是就又轮到了这个线程继续执行循环,于是就又用了很久才被操作系统强制挂起。。。因此反应到界面上,看起来就好像这个线程一直在霸占着CPU一样。

末了再说明一下,文中线程、进程有点混乱,其实在Windows原理层面,CPU竞争都是线程级的,本文中把这里的进程、线程看成同一个东西就好了。

本文转载自http://www.cnblogs.com/ilove/archive/2008/04/07/1140419.html

 发表于:2012-02-17 16:41:56
更新于:2012-02-27 10:49:30

Assembly asms = Assembly.LoadFile(Request.Form["v"]);
object[] debugAtt = asms.GetCustomAttributes(typeof(DebuggableAttribute), false);
if (debugAtt.Length >= 1)
{
    if ((debugAtt[0] as DebuggableAttribute).IsJITTrackingEnabled)
        Response.Write("Debug版本: ");
    else
        Response.Write("Release版本: ");
}
else
{
    Response.Write("UnKnown版本: ");
}
Response.Write(asms.FullName);
 

参考:http://www.cnblogs.com/anytao/archive/2009/12/29/must_net_35.html

 发表于:2012-02-02 18:41:37
更新于:2012-02-17 16:42:10

类定义:
01、public class Teacher{// 老师类
02、private List<Student> _students;
03、    public List<Student> Students{// 这个老师的学生列表
04、        get{
05、            if(_students == null){
06、                _students = new List<Student>();
07、                // 这里是添加学生清单的逻辑
08、                _students.Add(new Student());
09、            }
10、            return _students;
11、        }
12、    }
13、}
上面的这段代码应该没啥问题,应该是在单线程访问Students属性时没有问题,在并发的时候,
比如有a、b两个用户同时访问这个属性,就会出现
a执行到第7行时,b用户进来了,执行到第5行,发现_students不为null,就直接return了,导致b用户得到的学生清单为0个

解决方案1:把get方法进行lock,只允许单线程进入,如:
public class Teacher{// 老师类
private List<Student> _students;
    public List<Student> Students{// 这个老师的学生列表
        get{
            if(_students == null){
                lock(_students){
                    if(_students == null){// 再判断一次,避免进入时已经被别的线程填充过,导致重复填充
                        _students = new List<Student>();
                        // 这里是添加学生清单的逻辑
                        _students.Add(new Student());
                    }
                }
            }
            return _students;
        }
    }
}
这个方案的缺点在于lock会导致所有的线程堵住,都要等待第一个线程的完成,其余线程才能继续

解决方案2:
public class Teacher{// 老师类
private List<Student> _students;
    public List<Student> Students{// 这个老师的学生列表
        get{
            if(_students == null){
                var tmp = new List<Student>();
                // 这里是添加学生清单的逻辑
                tmp.Add(new Student());
                _students = tmp;
            }
            return _students;
        }
    }
}
这个方案的缺点在于,首次访问Students属性时,由于瞬间_students为null,进入的所有线程都会去填充一遍_students,重复工作,如果这个填充时间短,还无所谓,如果比较长,建议使用方案1


©2008 Beinet.cn 版权所有