c#多线程线程池快速入门

什么是多线程?

一个CPU如何实现多线程?当然是mock出来的。每个线程都有一定的优先级。对于优先级相同的线程A,B,CPU先执行A一个时间片(time slice),时间片很短,虽然大部分情况下A没有执行完,但是不再执行A,而是执行B一个时间片,然后再执行A一个time slice, 再执行B,  都执行完了,再分配给优先级低的线程。如果此时有和A,B同优先级的线程进入线程池,那么低优先级的线程只有继续等待,如果这种状态持续下去,那么low priority的thread就要"饿死"了。

 

线程的状态

当系统new一个thread的时候,状态就是Unstarted. 当一个线程开始执行的时候线程状态Running. C#里Thread.Start方法只是通知系统这个线程可以开始了,并不会立刻执行(也就是说状态是异步的变为Running)。

等待睡眠联合状态

哇塞,这个名字很骚逼的样子。别急,其实这个名字恰好描述了进入该状态的三种方式:

1) 调用类Monitor的Wait方法可以使当前线程进入WaitSleepJoin.当其他线程调用Monitor的Pulse方法时,下一个等待线程返回Running状态。当其他线程调用Monitor的PulseAll方法时,所有的等待线程返回Running状态。

2) 调用Thread的Sleep方法可以使当前线程进入WaitSleepJoin.当指定的睡眠时间结束时,睡眠线程返回Running状态。

3) 调用被依赖线程的Join方法可以使依赖线程进入WaitSleepJoin。

比如说主线程里start了一个subthread,要等subthread执行完了再继续主线程,那么subthread就是被依赖线程,主线程就是依赖线程,用法如下:

1         static void Main(string[] args)
2         {            
3             Thread subThread = new Thread(f);
4             subThread.Start();
5 
6             subThread.Join();
7             Console.WriteLine("SubThread finished");
8         }

调用Join之后,Main方法进入WaitSleepJoin状态,直到subthread完成,主线程返回Running状态。
In addition,如果等待或者睡眠线程的Interrupt方法被其他线程调用,那么所有等待和睡眠线程都返回Running状态。

后台线程:

后台线程其实跟线程的状态不是一个概念。但是ThreadState的枚举里面居然有他。 前台线程和后台线程的唯一的区别是后台线程不会阻止进程终止。Namely,只要有活着的前台线程,那进程就不能终止,跟有没有后台线程没关系。在默认情况下,用Thread 类创建的线程都是前台线程。线程池中的线程总是后台线程。

 

线程同步

 面试的话这个是重点。什么是线程同步?同步就是协同步调,别弄乱套了。比如说两个线程同时要操作一个变量,一个要加,一个要减,这很容易乱套。线程同步就是解决这类的问题。方法有如下几类:

1、Mutex类(互斥器),Monitor类,lock方法 。是的,当你了解了线程同步的概念,那么想到的第一个方法就是锁。Mutex类(互斥器)和Monitor类其实和锁差不多。具体详见http://hi.baidu.com/songwentao/item/36ec5a165735d124f6625ccc。连接中提到了“令牌”对象,怎么理解呢?code描述的是类,而程序运行的时候针对的是一个个具体的对象,是一把把的锁,只有同一把锁才会锁住。连接中lock和Monitor的“令牌”对象,Mutex类的实例(其实也是“令牌”对象),就是这一把把的锁。如果不是同一把锁,那锁定的code是无效的。如果还是比较模糊,请看我的大白话http://www.cnblogs.com/wangtian52127/archive/2012/01/20/2327949.html
2、ManualResetEvent类,AutoResetEvent类(这两个都是由EventWaitHandle类派生出来的。具体详见http://blog.csdn.net/yuechao20022/article/details/2807477

看完之后有什么感想?怎么没有锁呢?对,这是线程同步的另外一种情况。使多线程程序混乱的不只是并发,依赖的问题也需要妥善解决。
3、ReaderWriterLock类。这个很重要。具体详见http://www.csharpwin.com/dotnetspace/12761r5814.shtml。由于区分了读和写,而读锁是共享锁,因此提高了性能。读操作锁的本质是共享锁(也可以写操作,当然不推荐),可以有多个,并且当有读操作锁存在时,不能对该资源添加写操作锁(我们都在用呢,你改了我们读的就不对了)。写操作锁的本质是互斥锁(也可以读,当然不推荐),只能有一个,并且有写操作锁存在时,不能对该资源添加读操作锁(我要改东西了,你们先别读了,读了也是错的)。Plus:注意获取读写操作锁的try catch.

什么是线程安全?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

Interlocked类:Provides atomic operations for variables that are shared by multiple threads.

这个类提供了一些带有锁操作的简单方法以帮助程序员更方便的实现线程安全,当然他的方法本身就是线程安全的。

 

线程池

kind of like 数据库连接池,当一个线程结束之后不是真的销毁,而是以挂起状态返回线程池。如果应用程序再次向线程池发出请求,那么这个挂起的线程将激活并执行任务,而不会创建新线程,节约了很多开销。详见http://wenku.baidu.com/view/08461332ee06eff9aef80780.html。线程池的优势在于节约开销和控制最大并行线程的数量,这点在服务端编程作用明显,尤其是你有很多线程的时候。线程池中的线程由系统管理,程序员可以集中精力处理应用程序任务。文中介绍了三种线程入池的方法。其中最后一种介绍的有些模糊。其实RegisterWaitForSingleObject方法有2个作用,一个是文中所说的内核对象得到通知以启动线程,另外一个作用是时间控制启动线程(类似Timer)。而文中的例子其实是通过时间控制启动线程的。关于此方法请参考 http://hi.baidu.com/handboy/item/a5617d267cfce5d6a417b6ec。请大家理解一下第一个参数WaitHandle waitObject。他是个句柄,作用类似于前面提到的“令牌”对象。

posted @ 2012-08-27 17:05  非常苹果  阅读(528)  评论(1编辑  收藏  举报