多线程学习

多线程学习心得

                       

一:为什么要用线程:

        使用线程可以提高系统的性能,比如系统请求大量数据的时候,把一些工作交由异步线程去完成(如:输出工作),使主线程保持稳定去处理其他事情。

优点:提高系统的性能。

详细描述:

 (1)多线程技术使程序的响应速度更快 ,因为用户界面可以在进行其它工作的同时一直处于活动状态;

(2)当前没有进行处理的任务时可以将处理器时间让给其它任务;

(3)占用大量处理时间的任务可以定期将处理器时间让给其它任务;

(4)可以随时停止任务;

(5)可以分别设置各个任务的优先级以优化性能。

缺点:CPU开销增大。会出现线程死锁导致出错

详细描述:

(1)等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源 ,如打印机等。

(2)对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。当这种负担超过一定程度时,多线程的特点主要表现在其缺点上,比如用独立的线程来更新数组内每个元素。

(3)线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。

(4)对公有变量的同时读或写。当多个线程需要对公有变量进行写操作时,后一个线程往往会修改掉前一个线程存放的数据,从而使前一个线程的参数被修改;另外 ,当公用变量的读写操作是非原子性时,在不同的机器上,中断时间的不确定性,会导致数据在一个线程内的操作产生错误,从而产生莫名其妙的错误,而这种错误是程序员无法预知的。

 

什么情况下需要使用

是否需要创建多个线程取决于各种因素。在以下情况下,最适合采用多线程处理:
(1)耗时或大量占用处理器的任务阻塞用户界面操作;

(2)各个任务必须等待外部资源 (如远程文件或 Internet连接)。

 

注意:需要花费不少的时间在线程的切换上,所以过多地使用多线程反而会导致性能的下降。

 

推介浏览:http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html#t2

 

 

二:线程的基本知识

1.1System.Threading.Thread类基本属性

属性名称

说明

CurrentContext

获取线程正在其中执行的当前上下文。里面有个ContextID,和ContextProperties

属性,表示上下问ID,和上下文属性的数组

CurrentThread

获取当前正在运行的线程。

ExecutionContext

获取一个   ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。

(没用到过,不清楚)

IsAlive

获取一个值,该值指示当前线程的执行状态。

IsBackground

获取或设置一个值,该值指示某个线程是否为后台线程。

IsThreadPoolThread

获取一个值,该值指示线程是否属于托管线程池。

ManagedThreadId

获取当前托管线程的唯一标识符。

Name

获取或设置线程的名称。

Priority

获取或设置一个值,该值指示线程的调度优先级。

ThreadState

获取一个值,该值包含当前线程的状态。(Unstarted、Sleeping、Running)

 

1.2线程的优先级别

成员名称

说明

Lowest

可以将 Thread 安排在具有任何其他优先级的线程之后。

BelowNormal

可以将 Thread 安排在具有 Normal 优先级的线程之后,在具有 Lowest 优先级的线程之前。

Normal

默认选择。可以将 Thread 安排在具有 AboveNormal 优先级的线程之后,在具有 BelowNormal 优先级的线程之前。

AboveNormal

可以将 Thread 安排在具有 Highest 优先级的线程之后,在具有 Normal 优先级的线程之前。

Highest

可以将 Thread 安排在具有任何其他优先级的线程之前。

 

 

1.3System.Threading.Thread的方法

方法名称

说明

Abort()    

终止本线程。

GetDomain()

返回当前线程正在其中运行的当前域。

GetDomainId()

返回当前线程正在其中运行的当前域Id。

Interrupt()

中断处于   WaitSleepJoin 线程状态的线程。

Join()

已重载。 阻塞调用线程,直到某个线程终止时为止。

Resume()

继续运行已挂起的线程。

Start()  

执行本线程。

Suspend()

挂起当前线程,如果当前线程已属于挂起状态则此不起作用

Sleep()  

把正在运行的线程挂起一段时间。

 

 

2.1System.Threading 命名空间

类    

说明

AutoResetEvent

通知正在等待的线程已发生事件。无法继承此类。

ExecutionContext

管理当前线程的执行上下文。无法继承此类。

Interlocked

为多个线程共享的变量提供原子操作。

Monitor

提供同步对对象的访问的机制。

Mutex

一个同步基元,也可用于进程间同步。

Thread

创建并控制线程,设置其优先级并获取其状态。

ThreadAbortException

在对   Abort 方法进行调用时引发的异常。无法继承此类。

ThreadPool

提供一个线程池,该线程池可用于发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。

Timeout

包含用于指定无限长的时间的常数。无法继承此类。

Timer

提供以指定的时间间隔执行方法的机制。无法继承此类。

WaitHandle

封装等待对共享资源的独占访问的操作系统特定的对象。

 

 

2.2System.Threading中的包含了下表中的多个常用委托

委托

说明

ContextCallback

表示要在新上下文中调用的方法。

ParameterizedThreadStart

表示在   Thread 上执行的方法。

ThreadExceptionEventHandler

表示将要处理   Application 的   ThreadException 事件的方法。

ThreadStart

表示在   Thread 上执行的方法。

TimerCallback

表示处理来自   Timer 的调用的方法。

WaitCallback

表示线程池线程要执行的回调方法。

WaitOrTimerCallback

表示当   WaitHandle 超时或终止时要调用的方法。

 

 

线程的管理方式

通过ThreadStart来创建一个新线程是最直接的方法,但这样创建出来的线程比较难管理,如果创建过多的线程反而会让系统的性能下载。

有见及此,.NET为线程管理专门设置了一个CLR线程池,使用CLR线程池系统可以更合理地管理线程的使用。所有请求的服务都能运行于线程池中,当运行结束时线程便会回归到线程池。通过设置,能控制线程池的最大线程数量,在请求超出线程最大值时,线程池能按照操作的优先级别来执行,让部分操作处于等待状态,待有线程回归时再执行操作。

 

三.线程的使用

1. 使用ThreadStart委托

         应用于:最简单的方式,只需要传入一个调用的方法就行。

Thread thread = new Thread(new ThreadStart(message.ShowMessage));

// message.ShowMessage被调用的方法
thread.Start();

 

2. 使用ParameterizedThreadStart委托

      应用于:需要带参数的线程。参数为object

Thread thread = new Thread(new ParameterizedThreadStart(message.ShowMessage));

//先在里面传入一个要被调用的message.ShowMessage方法

Person person = new Person();

person.Name = "Jack";

person.Age = 21;

 thread.Start(person); 

//启动时,把要传递的参数传过去,这里是传一个定义好的Person对象。

 

3. 通过QueueUserWorkItem启动工作者线程

重构两个方法:

一为 ThreadPool.QueueUserWorkItem(WaitCallback)
二为 ThreadPool.QueueUserWorkItem(WaitCallback,Object)

区别:一个是不带参数,一个是带参数。

 

ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCallback),"Hello Elva");

//后面的传值"Hello Elva",回到state上通过转型获得

static void AsyncCallback(object state)

{

             Thread.Sleep(200);

             string data = (string)state;

             Console.WriteLine("Async thread do work!\n"+data);

}

 

QueueUserWorkItem限制

通过ThreadPool.QueueUserWorkItem启动工作者线程虽然是方便,但WaitCallback委托指向的必须是一个带有Object参数的无返回值方法,这无疑是一种限制。若方法需要有返回值,或者带有多个参数,这将多费周折。

 

在面对回调方法有返回值,带多个参数,需要使用到委托。

 

4. 委托类

IAsyncResult介绍

public interface IAsyncResult

 {

     object AsyncState {get;}   //获取用户定义的对象,它限定或包含关于异步操作的信息。

     WailHandle AsyncWaitHandle {get;}   //获取用于等待异步操作完成的 WaitHandle。

     bool CompletedSynchronously {get;}  //获取异步操作是否同步完成的指示。

     bool IsCompleted {get;}             //获取异步操作是否已完成的指示。

 }

 

4.1无参数,无回调函数情况。

1.先要定义一个委托

delegate string MyDelegate(string name);

2. 建立委托

MyDelegate myDelegate = new MyDelegate(Hello);

3.启动异步调用委托

IAsyncResult result=myDelegate.BeginInvoke("Leslie", null, null);

4.定义委托调用的方法

static string Hello(string nam)
{
         return "Hello " + name;
}

5. 判断异步委托是否完成。

while (!result.IsCompleted)

      {
              Thread.Sleep(200);      //虚拟操作
             Console.WriteLine("Main thead do work!");
      }

 

6.异步委托结束,获得异步返回值

string data=myDelegate.EndInvoke(result);

 

除此以外,也可以使用WailHandle完成同样的工作,WaitHandle里面包含有一个方法WaitOne(int timeout),它可以判断委托是否完成工作,在工作未完成前主线程可以继续其他工作。运行下面代码可得到与使用 IAsyncResult.IsCompleted 同样的结果,而且更简单方便 。

他们的使用区别

while (!result.AsyncWaitHandle.WaitOne(200))

==

while (!result.IsCompleted)

{
      Thread.Sleep(200);      //虚拟操作
}

 

当要监视多个运行对象的时候,使用IAsyncResult.WaitHandle.WaitOne可就派不上用场了。幸好.NET为WaitHandle准备了另外两个静态方法:WaitAny(waitHandle[], int)与WaitAll (waitHandle[] , int)。其中WaitAll在等待所有waitHandle完成后再返回一个bool值。而WaitAny是等待其中一个waitHandle完成后就返回一个int,这个int是代表已完成waitHandle在waitHandle[]中的数组索引。

使用方法:

WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle,........ };
while (!WaitHandle.WaitAll(waitHandleList,200))
{
                 Console.WriteLine("Main thead do work!");
}

 

4.2带有回调函数以及带参数的异步委托

1.定义一个参数对象Person

public class Person

{

    public string Name;

    public string Age;

}

2.定义一个委托:

delegate string MyDelegate(string name);

3.实例化一个委托,传递委托要调用的函数Hello

MyDelegate myDelegate=new MyDelegate(Hello);

4.实例化一个传递参数Person

Person person=new Person();

person.Name=”Elva”;

person.Age=27;

5.异步调用委托,输入参数对象person、获得计算结果。

myDelegate.BeginInvoke(“Leslive”,new AsyncCallback(Completed),person);

6.定义委托要调用的Hello函数

static string Helle(string name){…};

7.定义回调函数

static void Completed(IAsyncResult result)

{

    AsyncResult _result=(AsyncResult)result;

    MyDelegate myDelegate=(MyDelegate)_result.AsyncDelegate;

    string date=myDelegate.EndInvoke(_result);

    Person person=(Person)result.AsyncState;

    string message=person.Name+”’s age is”+person.Age.ToString();

    Console.WriteLine(data+”\n”+message);

}

 

ThreadPool相比Thread来说具备了很多优势

线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且还不能控制线程池中线程的开始、挂起、和中止。

 

任务的学习:

一:Task的优势

ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存一些使用上的不方便。比如:

1: ThreadPool不支持线程的取消、完成、失败通知等交互性操作;

2: ThreadPool不支持线程执行的先后次序;而任务支持

以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,FCL中提供了一个功能更强大的概念:Task。

Task 是在Threading类库里面抽象出来的一个类Threading.Tasks;

 

下面通过例子来说明任务的用法:

posted @ 2013-07-22 14:34  偏执的放纵  阅读(117)  评论(0编辑  收藏  举报