多线程 C#解决方案小结
一.不同级别(Level)上的同步:
1.object level 同步
对应的class必须从ContextBoundObject继承(同步上下文context,使所有的方法调用能被截获),并且在
class上运用SynchronizationAttribute 。
2.Method level 同步
System.Runtime.CompilerService空间包含的一些属性将影响CLR在运行期间的行为。特性MethodImplAttribute可以用于需要进行同步控制的方法上。
3.code segment level 同步
(1)Monitor类(主要是静态方法)
Monitor.Enter(obj)//获得加在对象obj上的锁
...
Monitor.Exit(obj)//释放锁
//上面两句之间的代码相当于lock(obj){...}
Monitor.TryEnter(obj)//该方法立即返回,如果返回值为false,则接下来不需要Monitor.Exit(obj)。
//以下几个方法用于线程间的交互 ==》 解决同步依赖
Monitor.Wait(obj)//等待脉冲消息。释放对象上的锁并阻塞当前线程,以后只有其它线程调用Pulse或PulseAll时才会给它再次获得锁的机会
Monitor.Pulse(obj)//发射脉冲消息( 只有得到锁后才能发射,而且发射不会自动释放锁)
Monitor.PulseAll(obj)
注意:
(1)Monitor 锁定对象,只能在Enter()和Exit()之间的代码块中调用Wait和Pulse
(2)不能在一个线程中获得锁,而在另一个线程中释放锁。这样会产生锁丢失。 获得锁和释放锁应该在同一个线程中完成。
(3)lock语句
lock(obj)
{
需要进行同步的代码
}
(4)ReaderWriterLock类
实现单写多读程序的锁。
AcquireReaderLock()//当没有写程序线程占用锁时,就可获得锁
AcquireWriterLock()//当没有任何读写程序线程占用锁时,才可获得锁
ReleaseReaderLock()
ReleaseWriterLock()
(5)ManualResetEvent
Set()方法将状态设置为有信号
Reset()将其设置为无信号
WaitOne()将阻塞到其有信号为止,若调用WaitOne的时刻就是有信号的,将不会阻塞
(6)AutoResetEvent
与ManualResetEvent的区别是,AutoResetEvent.WaitOne()会自动改变事件对象的状态,即AutoResetEvent.WaitOne()每执行一次,事件的状态就改变一次。有信号-->无信号;无信号-->有信号
说明:
(1)无论是Monitor还是lock、ReaderWriterLock都只对引用类型的对象有效,因为引用类型的对象有一个隐藏的sync#字段,该字段的作用就是作为加锁的标记。
(2)上述的各种设施中,只有Monitor 和ManualResetEvent/AutoResetEvent 能解决线程间的同步依赖问题,而其它的设施主要用于解决临界资源共享。
4.member level同步
(1)Interlocked类(主要是静态方法)
同步一个由许多线程共享的变量。
Decrement(ref int);//使变量减1
Increment(ref int);//使变量加1
//以上两个方法仅针对类int变量
Exchange(ref object, object);
(2)ThreadStaticAttribute
该特性用于修饰静态变量,被该特性修饰的静态变量在每个线程中都有自己的副本。
二.创建线程安全的对象
Hashtable h = Hashtable.Synchronized(new Hashtable()) ;
ArrayList等容器也提供类似操作。
1.每个窗体都有自己的都在不同的线程上运行,如果需要在窗体之间交互,就需要在线程之间交互。
2.当线程Sleep时,系统就退出执行队列一段时间,当睡眠结束时,系统会产生一个时钟中断,从而使线程回到执行队列中,从而恢复线程的执行。
3.如果父线程先于子线程结束,那么子线程将在父线程结束的同时被迫结束。Thread.Join()方法使父线程等待,直到子线程结束。 Abort()方法带来的后果是不可恢复的终止线程。
4.起始线程可以称之为主线程,如果所有的前台线程都停止了,那么主线程可以终止,而所有的后台线程都将无条件终止。
后台线程跟前台线程只有一个区别,那就是后台线程不妨碍程序的终止。一旦一个进程所有的前台线程都终止后,CLR 将通过调用任意一个存活中的后台进程的Abort()方法来彻底终止进程。
6.挂起,睡眠(都可称为--阻塞,暂停)
与Thread.Sleep 不同,Thread.Suspend 不会使线程立即停止执行。直到线程到达安全点之后它才可以将该线程挂起。如果线程尚
未启动或已经停止,则它将不能挂起。调用 Thread.Resume 将使另一个线程跳出挂起状态并使该线程继续执行。
一个线程不能对另一个线程调用Sleep ,但是一个线程可以对另一个线程调用Suspend。
还可以使用许多其它的方式来阻塞线程。例如,可以通过调用 Thread.Join 使一个线程等待另一个线程(子线程)停止。使用Monitor.Wait使一个线程等待访问一个同步对象。
5.关键字lock可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。
多线程公用一个对象时,就不应该使用lock关键字了,这里Monitor,Monitor提供了使线程共享资源的方案。
Monitor类可以锁定一个对象,一个线程只有得到这把锁才可以对该对象进行操作。
如:
Monitor.Enter(obj);
//现在oQueue对象只能被当前线程操纵了
Monitor.Exit(obj);
6.一个进程开始时至少会有一个主线程 ( 即主执行实例 ) ,这是在系统加载你的程序的时候所创建的主执行流程。
而消息队列则是与线程 ( Thread ) 相关的,在似win2k上一个线程有一个且只有一个消息队列 ( queue ) 与之相对应。
消息队列是在什么时候生成的呢? 在似win2k系统上,从一开始创建线程就已经有了。
一个线程可以创建多个窗体。统发送给这些窗口的消息都统一发送到同一个 消息队列 中,幸亏消息结构中有msg.hwnd指出该条消息与 哪一个窗口相关, DispatchMessage() 函数就是依照这个保证消息分派处理自动化而且不会出错!
7.每个窗体都属于创建它的线程,在一线程中直接访或间接问其它线程中的窗体的方法将导致运行时错误(VS2005)。
解决方法:使用窗体从Control继承而来的Control.Invoke(Delegate)方法。该方法将在创建窗体的线程上执行委托指向的方法。
注意:在VS2003下,可以在一个线程中直接或间接调用另一个线程中的窗体的方法,而不会导致运行时错误。
本文引用通告地址: http://blog.csdn.net/zhuweisky/services/trackbacks/429826.aspx