天天看点

C# 线程、任务和同步

1,线程概述

线程是程序汇中独立的指令流。线程有一个优先级,实际上正在处理的程序的位置计数器,一个存储其局部变量的栈。每个线程都有自己的栈。但应用程序的内存和堆由一个进程的所有线程共享。

进程包含资源,如windows句柄,文件句柄或其他内核对象。每个进程都分配了虚拟内存。一个进程至少包含一个线程。操作系统会调度线程。

总结:

同步代码区域(代码块):lock,  monitor, spinlock,

mutex,waithandle,semaphore,eventwaithandle,autorestevent/manualresetevent.

barrier,

readwriterlock(slim)

多线程变量同步:interlocked, 

进程间同步:

mutex, semaphore,

2,异步委托:

创建线程的一种简单方式是定义一个委托,并异步调用它。委托时方法类型安全的引用。delegate类还支持异步调用委托,在后头创建一个执行任务的线程。

  委托使用线程池来完成异步调用。

  public

delegate int takesawhiledelegate(int data, int ms);

2.1投票:

  iasyncresult

ar=al.begininvoke(1,3000, null, null);

      int

result=dl.endinvoke(ar);

2.2 等待句柄  (waithandle)

C# 线程、任务和同步
C# 线程、任务和同步

投票(ar.iscompleted)

或者等待句柄(ar.asyncwaithandle.waitone(50, false)

2.3 异步回调 (dl.begininvoke(1,3000, takesawhilecompleted, dl)

)

传入一个回调函数委托,来异步执行。

C# 线程、任务和同步
C# 线程、任务和同步

回调方法

3,thread类

3.1

给线程传递数据

  1,使用带parameterizedthreadstart委托参数的thread构造函数。2,创建自定义类,把线程的方法定位实例方法,这样就可以初始化实例的数据,之后启动线程。

3.2

后台线程:

  只要有一个前台线程在运行,应用程序的进程就在运行。如果多个前台线程在运行,而main()方法结束了,应用程序的进程依然是激活的,直到所有前台线程完成其任务为止。

  

C# 线程、任务和同步
C# 线程、任务和同步

前台线程示例

3.3 线程的优先级 

4 线程池

5,任务

5.1 启动任务:

C# 线程、任务和同步

启动任务代码

C# 线程、任务和同步

 5.2 连续的任务

task t1=new task(doonfirst);

task

t2=t1.continuewith(doonsecond);

5.3任务层次结构

6 parallel

parallel.for

parallel.foreach()

parallel.invoke(fun1,fun2);

C# 线程、任务和同步
C# 线程、任务和同步

parallel.invoke code

7. 取消架构

8, 线程问题:  争用条件和死锁

8.1 争用条件:

C# 线程、任务和同步
C# 线程、任务和同步

死锁演示代码

9, 同步

9.1 lock

语句

栈是线程独立的,但不是私有的。所有线程的栈内所有内容,都可以被其他线程访问。

为什么不用 lock(this)

?

因为这通常超出我们的控制,因为其他人也有可能lock这个对象。一个私有的对象是更好的选择。避免lock一个公开类型,或者超出你代码的控制的实例。

tips:可以提供线程安全的原子操作。

C# 线程、任务和同步
C# 线程、任务和同步

线程不安全-问题代码

9.2

interlocked类

interlock类用于使变量的简单语句原子化,线程安全方式递增、递减、交换和读取。i++不是线程安全的(包含3个操作:从内存获取、递增1、存储回内存,这些操作都可以被线程调度器打断)。

9.3 monitor类

lock语句由编译器解释为monitor类: moniter.enter(obj) ;  

monitor.exit(obj);

monitor类的一个优点:可以添加一个等待被锁定的超市值。monitor.tryenter(lockobj,500,ref

locktoken);

C# 线程、任务和同步
C# 线程、任务和同步

monitor.tryenter

 9.4

spinlock结构

适合于有大量的锁定,而且锁定的时间非常短。用法非常接近于monitor类。获得锁使用enter()或者tryenter(),释放锁使用exit()方法。小心spinlock的传送,因为是结构,所以会复制。

9.5 waithandle基类

delegate begininvoke()

用waithandle.waitone(50,false)来bolck当前线程,

waithandle是一个抽象基类,用于等待一个信号量的设置。可以等待不同的信号,因为waithandle是一个基类,可以派生一些类。

C# 线程、任务和同步
C# 线程、任务和同步

asyncwaithandle

waitone() 等待一个,waitall()等待多个对象,waitany等待多个对象的一个。waitall和waitany是静态方法。

waithandle基类有一个safewaithandle属性,其中可以将本机句柄赋予一个操作系统资源,并等待该句柄。

mutex、eventwaithandle

和 semaphore类继承自waithandle基类。所以可以等到使用它们。

9.6 mutex类

mutex(mutual

exclusion,互斥)是.net

framework中提供多个集成同步访问的一个类。它非常类似于monitor,只有一个线程能拥有锁定。只有一个线程能获得互斥锁定,访问受互斥访问的同步代码区域。

在mutex类的构造函数中,可以指定互斥是否最初由主调线程拥有。定义互斥的名称,获得互斥是否存在的信息。

系统能识别有名称的mutex

C# 线程、任务和同步
C# 线程、任务和同步

mutex

9.7

semaphore类

信号量非常类似于互斥,其区别是多个线程使用。信号量是一种技术的互斥锁定。使用信号量可以定义同时访问旗语锁定保护的资源的线程个数。

semaphore类:可以命名,使用系统范围内的资源,允许不同进程间同步。

C# 线程、任务和同步
C# 线程、任务和同步

semaphore(多线程&跨进程同步)

semaphoreslim类是对于较短等待时间进行了优化的轻型版本,不能跨进程。不能命名,不使用内核信号量,不能跨进程。

C# 线程、任务和同步
C# 线程、任务和同步

semaphoreslim

9.8 event类

事件是另一个系统范围内的资源同步方法。为了从托管代码中使用系统事件,.net

framework提供了manualresetevent、autoresetevent、manualreseteventslim和countdownevent类。

C# 线程、任务和同步
C# 线程、任务和同步

manualresetevent

把任务分支到多个任务中,并在以后合并结果,使用新的countdownevent类很有用。

不需要位每个任务创建一个单独的事件对象,而只需要创建一个事件对象。

var mevents = new manualreseteventslim[taskcount];

// var

cevent = new countdownevent(taskcount);

var waithandles = new waithandle[taskcount];

var

calcs = new calculator[taskcount];

int index = waithandle.waitany(waithandles);//wait

//tasks

mevent.set();//all thread set;

//continue

C# 线程、任务和同步
C# 线程、任务和同步

countdownevent

 9.9 barrier 类

适合于工作有多个任务分支且以后又需要合并工作的情况。

 var barrier = new barrier(numbertasks +

1);

 barrier.signalandwait();//wait

barrier.removeparticipant();//2

left

barrier.removeparticipant();//1 left

//continue.

9.10

readwriterlockslim类

允许多个读取器。同时只有一个写入器工作,此时读取器不能工作。

C# 线程、任务和同步
C# 线程、任务和同步

readwriterlockslim

10 timer类