多线程

Rock Li 2008-12-4

   多线程编程是充分利用 CPU 的线程时间片来处理,可以做到在 doule kernel cpu 上使用,提高效率。

         多线程编程编写应用程序有一下的需要了解:

线程概述

使用委托的轻型线程

线程类

线程池

线程问题

同步技术(这个另外一篇介绍)

 

线程概述:

         线程是程序中独立的指令流,编写程序的时候都是有一个入口程序, Main() 方法 , 程序从 Main() 中的第一句开始执行,知道这个方法返回为止,这就是一个完整的线程。

         但是为了使得程序能够实时响应,例如 IE 浏览器,就是有多个线程,多线程是提高软件性能的一个重要饿方法。

异步委托:

         创建一个线程的简单的方式是定义一个委托,异步调用它,委托是方法的类型的安全的引用, Delegate 类还支持异步调用的方法。

         下面给出异步特性显示的例子。

Static int TakesAWhile(int date,int ms)

{

         Console.writeline(“TakesWhile started!”);

         Thread.Sleep(500);

         Console.WriteLine(“TakesAWhile completed!”);

         return ++data;

}

要在委托中调用这个方法,就必须定义一个和方法有相同的参数和返回值的委托类。例如定义上面方法的委托类。

Pulbic delegate int TakesAWhileDelegate(int date,int ms );

现在具有可以使用不同的技术异步调用委托,返回结果!

   异步技术有以下这些种:

 

投票:

         public delegate int TakeAWhileDelegate (int data,int ms );

    class Program

    {

        static void Main(string [] args)

        {

            TakeAWhileDelegate dlg = TakeAWhileFunction;   //

            IAsyncResult ar = dlg.BeginInvoke(1,3000,null ,null );

            Console .WriteLine("The delegate has been doing now !" );

 

            while (!ar.IsCompleted)

            {    Console .Write("." );

                Thread .Sleep(50);              

            }

 

            int result = dlg.EndInvoke(ar);

            Console .WriteLine("Result : {0}" , result);

 

        }

        public static int TakeAWhileFunction(int data, int ms)

        {

            Console .WriteLine("TakeAWhileFunction started!" );

            System.Threading.Thread .Sleep(ms);

            Console .WriteLine("TakeAWhileFunction completed!" );

            return ++data;

        }

        Public static void CallBackFunction(IAsyncResult ar)

        {

            Console.WriteLine();   

}

    }

   

经常看到名为 BeginXXX EndXXX 的方法,他们是做什么用的  

这是.net 的一个异步方法名称规范
  .Net 在设计的时候为异步编程设计了一个异步编程模型(APM ),这个模型不仅是使用.NET 的开发人员使用,.Net 内部也频繁用到,比如所有的Stream 就有BeginReadEndReadSocket,WebRequet,SqlCommand 都运用到了这个模式,一般来讲,调用BegionXXX 的时候,一般会启动一个异步过程去执行一个操作,EndEnvoke 可以接收这个异步操作的返回,当然如果异步操作在EndEnvoke 调用的时候还没有执行完成,EndInvoke 会一直等待异步操作完成或者超时。

.Net 的异步编程模型(APM )一般包含BeginXXXEndXXXIAsyncResult 这三个元素,BeginXXX 方法都要返回一个IAsyncResult ,而EndXXX 都需要接收一个IAsyncResult 作为参数.

BeginXXX EndXXX 中的XXX ,一般都对应一个同步的方法, 比如FileStreamRead 方法是一个同步方法,相应的BeginRead(),EndRead() 就是他的异步版本,HttpRequestGetResponse 来同步接收一个响应,也提供了BeginGetResponseEndGetResponse 这个异步版本,而IAsynResult 是二者联系的纽带,只有把BeginXXX 所返回的IAsyncResult 传给对应的EndXXXEndXXX 才知道需要去接收哪个BeginXXX 发起的异步操作的返回值。

 

 

 

 

 

线程池

线程池的作用:减少线程开启和销毁的开销。

创建线程涉及用户模式和内核模式的切换,内存分配,dll 通知等一系列过程,线程销毁的步骤也是开销很大的,所以如果应用程序使用了完一个线程,我们能把线程暂时存放起来,以备下次使用,就可以减小这些开销

 

线程池的使用可以这样简单的归纳:

  如果有许多的小任务需要完成,我们不建议使用Thread 来创建,因为这样会带来系统的很大的开销,我们可以使用线程池来做简单的代替,但是线程池是简单也就意味着它的功能就要简单的。

ThreadPool 类来管理线程池,这个类会在需要增加线程池中的数个时增加知道最大的线程数,我们可以获取系统支持的最大的线程数ThreadPool.GetMaxThreads(out nWorkerThreads,out nCompletionPortThreads) 来获取工作线程和IO 线程。简单的实例:

Static void main()

{

    Int nWorkerThreads;

    Int nCompletionPortThreads;

    ThreadPool.GetMaxThreads(out nWorkerThreads,out nComPletionPortThreads);

Console.writeline(“Max worker threads is:{0},IO Threads is {1}”,nWorkerThreads,nCompletionPortThreads);

For(int i=0;i<5;i++)

{

    ThreadPool.QueueUserWorkItem(JobForAThread);

}

Thread.Sleep(500);

 

}

Static void JobForAThread(object state)

{

    For(int i=0;i<3;i++)

{

    Console.writeline(“Loop {0},running inside pooled thread{1}”,i,Thread.CurrentThread.ManagedThreadID);

Thread.Sleep(50);

}

}

 

 

这样我们可以建立很多的小线程,由系统去分配,减小系统的开销,当线程数达到了Max 时,就必须等待。

 

 

 

后台线程:

    只需要有一个前台线程在运行,应用程序的进程就在运行,如果有多个前台线程在运行,而Main() 方法结束了,应用程序就是激活的状态,必须等待所有的前台线程执行完毕才完成任务。

.Net 中默认的Thread 创建的线程都是前台线程,线程池中的线程都是后台线程,我们可以在创建线程的时候Thread 设置属性的方式来设置IsBackground 来设置前台后台线程!

 

Thread 类:

    使用Thread 可以创建和控制线程。

如何给线程传递数据:

    需要给线程传递一些数据,可以采用两种方法,一种是利用Thread 自带的ParameterizedThreadStart 委托参数的Thread 构造函数,另外一种是创建一个定制的线程类,把线程的方法定义为实例方法,这样就可以初始化实例的数据,之后就启动线程。

首先定义传递给的数据结构:

Public struct data

{

    Public string message;

    //

}

如果使用 ParameterizedThreadStart 委托,线程的入口点必须有一个 object 类型的参数,返回类型是 void , 对象可以转换为数据,例如:

Static void ThreadMainWithParameters(object o)

{

          Data d=(Data)o;

Console.writeline(“Running in a thread,received {0}”,d.Message);

}

主程序中

Static void main()

{

          Data d =new Data();

          d.Message=”Info”;

          Thread t2=new Thread(ThreadMainWithParameters);

          T2.Start(d);

}

这样就把参数传递给新的线程了。

另外一种方法就是定义一个类,将线程中的主方法定义为类的一个实例方法:

Public class MyThread

{

          Private string data;

          Pulibc MyThread(string data)

          {

                   This.data=data;

          }

          Pulbic void ThreadMain()

          {

                   Console.writeline(“Running in a thread,data:{0}”,data);

}

}

这样就创建一个 MyThread 的一个对象,给 Thread 类的构造函数传递对象和 ThreadMain() 方法,这样线程就可以访问数据:

MyThread obj=new MyThread(“Info”);

Thread t3=new Thread(obj.ThreadMain);

T3.Start();

这样就可以开启一个线程了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值