现在的位置: 首页 > 综合 > 正文

多线程

2018年04月05日 ⁄ 综合 ⁄ 共 4435字 ⁄ 字号 评论关闭



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
就有BeginRead
EndRead
Socket,WebRequet,SqlCommand
都运用到了这个模式,一般来讲,调用BegionXXX
的时候,一般会启动一个异步过程去执行一个操作,EndEnvoke
可以接收这个异步操作的返回,当然如果异步操作在EndEnvoke
调用的时候还没有执行完成,EndInvoke
会一直等待异步操作完成或者超时。

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

BeginXXX
EndXXX
中的XXX
,一般都对应一个同步的方法,
比如FileStream
Read
方法是一个同步方法,相应的BeginRead(),EndRead()
就是他的异步版本,HttpRequest
GetResponse
来同步接收一个响应,也提供了BeginGetResponse
EndGetResponse
这个异步版本,而IAsynResult
是二者联系的纽带,只有把BeginXXX
所返回的IAsyncResult
传给对应的EndXXX
EndXXX
才知道需要去接收哪个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();

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

 

【上篇】
【下篇】

抱歉!评论已关闭.