先表明,向作者致敬http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html 风尘浪子
前半部分是复制风尘浪子的,从 三 开始,互联网收集整理. 感谢互联网,感谢open source. 重要是,大家能够领悟,掌握和运用多线程的知识.
1. 1 进程、应用程序域与线程的关系
进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。进程之间是相对独立的,一个进程无法访问另一个 进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进 程可以理解为一个程序的基本边界。
应用程序域(AppDomain)是一个程序运行的逻辑区域,它可以视为一个轻量级的进程,.NET的程序集正是在应用程序域中运行的,一个进程可 以包含有多个应用程序域,一个应用程序域也可以包含多个程序集。在一个应用程序域中包含了一个或多个上下文context,使用上下文CLR就能够把某些 特殊对象的状态放置在不同容器当中。
线程(Thread)是进程中的基本执行单元,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法 作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状 态信息。
进程、应用程序域、线程的关系如下图,一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。
在单CPU系统的一个单位时间(time slice)内,CPU只能运行单个线程,运行顺序取决于线程的优先级别。如果在单位时间内线程未能完成执行,系统就会把线程的状态信息保存到线程的本地 存储器(TLS) 中,以便下次执行时恢复执行。而多线程只是系统带来的一个假像,它在多个单位时间内进行多个线程的切换。因为切换频密而且单位时间非常短暂,所以多线程可 被视作同时运行。
适当使用多线程能提高系统的性能,比如:在系统请求大容量的数据时使用多线程,把数据输出工作交给异步线程,使主线程保持其稳定性去处理其他问题。但需要注意一点,因为CPU需要花费不少的时间在线程的切换上,所以过多地使用多线程反而会导致性能的下降。
2.1 System.Threading.Thread类
System.Threading.Thread是用于控制线程的基础类,通过Thread可以控制当前应用程序域中线程的创建、挂起、停止、销毁。
它包括以下常用公共属性:
属性名称 | 说明 |
---|---|
CurrentContext | 获取线程正在其中执行的当前上下文。 |
CurrentThread | 获取当前正在运行的线程。 |
ExecutionContext | 获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。 |
IsAlive | 获取一个值,该值指示当前线程的执行状态。 |
IsBackground | 获取或设置一个值,该值指示某个线程是否为后台线程。 |
IsThreadPoolThread | 获取一个值,该值指示线程是否属于托管线程池。 |
ManagedThreadId | 获取当前托管线程的唯一标识符。 |
Name | 获取或设置线程的名称。 |
Priority | 获取或设置一个值,该值指示线程的调度优先级。 |
ThreadState | 获取一个值,该值包含当前线程的状态。 |
2.1.1 线程的标识符
ManagedThreadId是确认线程的唯一标识符,程序在大部分情况下都是通过Thread.ManagedThreadId来辨别线程的。 而Name是一个可变值,在默认时候,Name为一个空值 Null,开发人员可以通过程序设置线程的名称,但这只是一个辅助功能。
2.1.2 线程的优先级别
.NET为线程设置了Priority属性来定义线程执行的优先级别,里面包含5个选项,其中Normal是默认值。除非系统有特殊要求,否则不应该随便设置线程的优先级别。
成员名称 | 说明 |
---|---|
Lowest | 可以将 Thread 安排在具有任何其他优先级的线程之后。 |
BelowNormal | 可以将 Thread 安排在具有 Normal 优先级的线程之后,在具有 Lowest 优先级的线程之前。 |
Normal | 默认选择。可以将 Thread 安排在具有 AboveNormal 优先级的线程之后,在具有 BelowNormal 优先级的线程之前。 |
AboveNormal | 可以将 Thread 安排在具有 Highest 优先级的线程之后,在具有 Normal 优先级的线程之前。 |
Highest | 可以将 Thread 安排在具有任何其他优先级的线程之前。 |
2.1.3 线程的状态
通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。
前面说过,一个应用程序域中可能包括多个上下文,而通过CurrentContext可以获取线程当前的上下文。
CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。
2.1.4 System.Threading.Thread的方法
Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,以后来的例子中会经常使用。
方法名称 | 说明 |
---|---|
Abort() | 终止本线程。 |
GetDomain() | 返回当前线程正在其中运行的当前域。 |
GetDomainId() | 返回当前线程正在其中运行的当前域Id。 |
Interrupt() | 中断处于 WaitSleepJoin 线程状态的线程。 |
Join() | 已重载。 阻塞调用线程,直到某个线程终止时为止。 |
Resume() | 继续运行已挂起的线程。 |
Start() | 执行本线程。 |
Suspend() | 挂起当前线程,如果当前线程已属于挂起状态则此不起作用 |
Sleep() | 把正在运行的线程挂起一段时间。 |
2.1.5 开发实例
以下这个例子,就是通过Thread显示当前线程信息
static void Main(string[] args)
{
Thread thread = Thread.CurrentThread;
thread.Name = "Main Thread";
string threadMessage = string.Format("Thread ID:{0}\n Current AppDomainId:{1}\n " +
"Current ContextId:{2}\n Thread Name:{3}\n " +
"Thread State:{4}\n Thread Priority:{5}\n",
thread.ManagedThreadId, Thread.GetDomainID(), Thread.CurrentContext.ContextID,
thread.Name, thread.ThreadState, thread.Priority);
Console.WriteLine(threadMessage);
Console.ReadKey();
}
3.1使用ThreadStart
static void Main(string[] args)
{
//Thread th = new Thread(Sleep);
Thread th = new Thread(new ThreadStart(Sleep));
th.Start();
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
}
//模拟执行长时间的任务
static void Sleep()
{
ThreadMessage("Sleep");
Console.WriteLine("我还没有执行完呢,请耐心等待....");
Thread.Sleep(3000);
}
static void ThreadMessage(string data)
{
string message = string.Format("ThreadName is {0} ThreadId is:{1}",
data, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
运行后你会发现,Sleep()方法 还没有执行完(睡眠3分钟模拟长时间任务),主线程已经执行完成. 这就是多线程的好处.
3.2 ParameterizedThreadStart 带参数的
static void Main(string[] args)
{
//Thread th = new Thread(Sleep);
Thread th = new Thread(new ParameterizedThreadStart(Sleep));
th.Start("Sleep");
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
}
//模拟执行长时间的任务
static void Sleep(object state)
{
string name = (string)state;
ThreadMessage(name);
Console.WriteLine("我还没有执行完呢,请耐心等待....");
Thread.Sleep(3000);
}
static void ThreadMessage(string data)
{
string message = string.Format("ThreadName is {0} ThreadId is:{1}",
data, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
3.3 匿名函数(委托)在多线程的运用
想必大家发现了这2个例子中都有一句注释的Thread th = new Thread(Sleep),这是为什么呢? 你可以把注释打开,下面的th注释掉,运行下,你会发现,两次都一样,这又是为什么呢?因为在实例化th的时候,有4个重载方法,其中2个就是ThreadStart和ParameterizedThreadStart
我们都知道ThreadStart和ParameterizedThreadStart和2个委托,而委托最大的作用就是传递一个方法, 在C#2.0就引入了匿名方法(3.0以及更高,lambda表达式取代了匿名方法)我们来看看匿名方法在这里能给我们带来点什么惊喜(方便)
class Program
{
static void Main(string[] args)
{
//Thread th = new Thread(delegate() { //在new ThreadStart()中
// ShowMessage("ShowMessage");
//});
//Thread th = new Thread(new ThreadStart(delegate() {
// ShowMessage("ShowMessage");
//}));
//Thread th = new Thread(new ThreadStart(() =>
//{
// ShowMessage("ShowMessage");
//}));
Thread th = new Thread(delegate(object o) //new ParameterizedThreadStart()中的
{
ShowMessage("ShowMessage");
});
//Thread th = new Thread(new ParameterizedThreadStart(delegate(object o) {
// ShowMessage("ShowMessage");
//}));
//Thread th = new Thread(t => ShowMessage("show"));
th.Start();
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
}
//模拟执行长时间的任务
static void Sleep(object state)
{
string name = (string)state;
ThreadMessage(name);
Console.WriteLine("我还没有执行完呢,请耐心等待....");
Thread.Sleep(3000);
}
//这里的参数不为object,你可以定义任何类型,任何参数
static void ShowMessage(string message)
{
ThreadMessage(message);
Console.WriteLine("我还没有执行完呢,请耐心等待....");
Thread.Sleep(3000);
}
static void ThreadMessage(string data)
{
string message = string.Format("ThreadName is {0} ThreadId is:{1}",
data, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
}
}
上面的几种方式都可以用. 我为什么要用个ShowMessage(string msg) 方法来测试呢 ? 细心你会发现ParameterizedThreadStart委托他定义的参数为object,用匿名函数就可以解决这问题.如果你对委托,匿名函数不太熟悉的话,你就要补习一下关于委托的知识了.
3.4 前台线程and后台线程
注意以上两个例子都没有使用Console.ReadKey(),但系统依然会等待异步线程完成后才会结束。这是因为使用Thread.Start()启动的线程默认为前台线程,而系统必须等待所有前台线程运行结束后,应用程序域才会自动卸载。
在第二节曾经介绍过线程Thread有一个属性IsBackground,通过把此属性设置为true,就可以把线程设置为后台线程!这时应用程序域将在主线程完成时就被卸载(主线程关闭),而不会等待异步线程的运行。
3.5 线程的一些方法
Thread.Sleep()大家都很熟悉了,休眠多长时间,里面是毫秒.1秒=1000毫秒. Join() 表面意思是把线程加入,也就是这线程完事后主线程才被卸载.
你可以把子线程设置成后台线程,然后调用这个方法,就不会发现’一闪而过’的现象了. Thread.Suspend()与 Thread.Resume()是在Framework1.0 就已经存在的老方法了,它们分别可以挂起、恢复线程。但在Framework2.0中就已经明确排斥这两个方法。这是因为一旦某个线程占用了已有的资源,再使用Suspend()使线程长期处于挂起状态,当在其他线程调用这些资源的时候就会引起死锁!所以在没有必要的情况下应该避免使用这两个方法。
若想终止正在运行的线程,可以使用Abort()方法。在使用Abort()的时候,将引发一个特殊异常 ThreadAbortException 。
若想在线程终止前恢复线程的执行,可以在捕获异常后 ,在catch(ThreadAbortException ex){…} 中调用Thread.ResetAbort()取消终止。
下面的例子是 终止线程和取消终止的例子(拷贝风尘浪子的)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main threadId is:" +
Thread.CurrentThread.ManagedThreadId);
Thread thread = new Thread(new ThreadStart(AsyncThread));
thread.Start();
Console.ReadKey();
}
//以异步方式调用
static void AsyncThread()
{
try
{
string message = string.Format("\nAsync threadId is:{0}",
Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
for (int n = 0; n < 10; n++)
{
//当n等于4时,终止线程
if (n >= 4)
{
Thread.CurrentThread.Abort(n);
}
Thread.Sleep(300);
Console.WriteLine("The number is:" + n.ToString());
}
}
catch (ThreadAbortException ex)
{
//输出终止线程时n的值
if (ex.ExceptionState != null)
Console.WriteLine(string.Format("Thread abort when the number is: {0}!",
ex.ExceptionState.ToString()));
//取消终止,继续执行线程
Thread.ResetAbort();
Console.WriteLine("Thread ResetAbort!");
}
//线程结束
Console.WriteLine("Thread Close!");
}
}
到此,我们学会了运用多线程,可不能沾沾自喜,这可只是刚刚开始. 前面说了通过ThreadStart创建的线程比较难管理,创建过多性能也会下降.主要是因为ThreadStart创建的线程不能循环利用,比如我们For循环个list,每一个model都开启个线程去执行任务,当前面的线程执行完了,也就销毁了,后面的还是要重新创建,不停的创建线程是很耗时的.由此可见 .NET为线程管理专门设置了一个CLR线程池.
4.1 关于CLR线程池
使用ThreadStart与ParameterizedThreadStart建立新线程非常简单,但通过此方法建立的线程难于管理,若建立过多的线程反而会影响系统的性能。
有 见及此,.NET引入CLR线程池这个概念。CLR线程池并不会在CLR初始化的时候立刻建立线程,而是在应用程序要创建线程来执行任务时,线程池才初始 化一个线程。线程的初始化与其他的线程一样。在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时, 线程池里挂起的线程就会再度激活执行任务。这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销.
4.2 工作者线程与I/O线程
CLR线程池分为工作者线程(workerThreads)与I/O线程 (completionPortThreads) 两种,工作者线程是主要用作管理CLR内部对象的运作,I/O(Input/Output) 线程顾名思义是用于与外部系统交换信息,IO线程的细节将在下一节详细说明。
通过ThreadPool.GetMax(out int workerThreads,out int completionPortThreads )和 ThreadPool.SetMax( int workerThreads, int completionPortThreads)两个方法可以分别读取和设置CLR线程池中工作者线程与I/O线程的最大线程数。在 Framework2.0中最大线程默认为25*CPU数,在Framewok3.0、4.0中最大线程数默认为250*CPU数,在近年 I3,I5,I7 CPU出现后,线程池的最大值一般默认为1000、2000。
若想测试线程池中有多少的线程正在投入使用,可以通过ThreadPool.GetAvailableThreads( out int workerThreads,out int completionPortThreads ) 方法。
使用CLR线程池的工作者线程一般有两种方式,一是直接通过 ThreadPool.QueueUserWorkItem() 方法,二是通过委托(异步操作是加入在线程池中的),下面将逐一细说。
4.3 通过QueueUserWorkItem启动工作者线程
ThreadPool线程池中包含有两个静态方法可以直接启动工作者线程:
一为 ThreadPool.QueueUserWorkItem(WaitCallback)
二为 ThreadPool.QueueUserWorkItem(WaitCallback,Object)
先把WaitCallback委托指向一个带有Object参数的无返回值方法,再使用 ThreadPool.QueueUserWorkItem(WaitCallback) 就可以异步启动此方法,此时异步方法的参数被视为null 。
class Program
{
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Sleep));
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
Console.ReadKey();
}
static void Sleep(object state)
{
ThreadMessage("sleep");
Console.WriteLine("我还没有执行完呢,请耐心等待....");
Thread.Sleep(3000);
}
static void ThreadMessage(string data)
{
string message = string.Format("ThreadName is {0} ThreadId is:{1}",
data, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
}
这是个不带参数的, ThreadPool.QueueUserWorkItem()有2个参数,第二个是个object类型
class Program
{
static void Main(string[] args)
{
Person person = new Person()
{
Name = "Somnus",
Age = 10
};
ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
Console.ReadKey();
}
static void ShowMessage(object state)
{
Person person = (Person)state;
Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age);
Thread.Sleep(3000);
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
//public WaitHandle Wait { get; set; }
}
我们创建了一个Person类,在加入线程池的时候,我们传了一个person , 在ShowMessage()方法中,得到传过来的person,并显示人的信息.
那在这里,我们可以效仿3.3匿名函数在线程池中的应用呢? 答案是可以的.
4.4 线程池中的异常处理
多线程执行任务的时候,有时候总是要出错嘛, 重要的是,我们能catch的住异常.
class Program
{
static void Main(string[] args)
{
Person person = new Person()
{
Name = "Somnus",
Age = 10
};
try
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person);
}
catch (Exception ex)
{
//写日志
}
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
Console.ReadKey();
}
static void ShowMessage(object state)
{
Person person = (Person)state;
throw new Exception("这里出错了,快catch住我");
Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age);
Thread.Sleep(3000);
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
//public WaitHandle Wait { get; set; }
}
这样? No~ 结果会差强人意的,我们并没有catch住他. 那该在哪try呢. 对, 在委托调用那方法中. 我们这样干,
class Program
{
static void Main(string[] args)
{
Person person = new Person()
{
Name = "Somnus",
Age = 10
};
ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), person);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
Console.ReadKey();
}
static void ShowMessage(object state)
{
try
{
Person person = (Person)state;
throw new Exception("这里出错了,快catch住我");
Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age);
Thread.Sleep(3000);
}
catch (Exception ex)
{
//写日志
}
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
//public WaitHandle Wait { get; set; }
}
这样,程序就不会报错了.
如果有好几个任务方法, 那我们要写好几个try{}catch{}这样肯定不是我们想要的,看看下面的封装
class Program
{
static void Main(string[] args)
{
Person person = new Person()
{
Name = "Somnus",
Age = 10
};
ThreadExecutor.Execute(new WaitCallback(ShowMessage), person);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
Console.ReadKey();
}
static void ShowMessage(object state)
{
Person person = (Person)state;
throw new Exception("这里出错了,快catch住我");
Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age);
Thread.Sleep(3000);
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
//public WaitHandle Wait { get; set; }
}
public class ThreadExecutor
{
public static bool Execute(System.Threading.WaitCallback callback, object state)
{
try
{
return System.Threading.ThreadPool.QueueUserWorkItem((data) =>
{
try
{
callback(data);
}
catch (Exception ex)
{
//写日志
}
}, state);
}
catch (Exception e)
{
//写日志
}
return false;
}
public static bool Execute(System.Threading.WaitCallback callback)
{
try
{
return System.Threading.ThreadPool.QueueUserWorkItem((data) =>
{
try
{
callback(data);
}
catch (Exception ex)
{
//写日志
}
});
}
catch (Exception e)
{
//写日志
}
return false;
}
}
其中Execute()方法就是把ThreadPool.QueueUserWorkItem用匿名函数的方法进行了封装,catch住了异常.
我们在前面说过了,线程池中的线程,默认是后台线程,前面几个例子,最后都有句Console.ReadKey(),就是如果不手动关闭主程序,主程序是不会自动关的.
这肯定不是我们想要的,那我们怎么判断所有子线程运行完毕后,关闭主线程呢
4.5 WaitHandle.WaitAll() 等待所有子线程完成后,关闭主线程 多个线程之间的协调工作
class Program
{
static void Main(string[] args)
{
WaitHandle[] waits = new WaitHandle[2]
{
new AutoResetEvent(false),
new AutoResetEvent(false)
};
Person person = new Person()
{
Name = "Somnus",
Age = 10,
Wait=waits[0]
};
Person person2 = new Person()
{
Name = "cnblogs",
Age = 20,
Wait = waits[1]
};
ThreadExecutor.Execute(new WaitCallback(ShowMessage),person);
ThreadExecutor.Execute(new WaitCallback(ShowMessage), person2);
for (int i = 0; i < 10; i++)
{
Console.WriteLine("这里是主线程在工作" + i);
}
WaitHandle.WaitAll(waits);
//WaitHandle.WaitAny(waits);
}
static void ShowMessage(object state)
{
Person person = (Person)state;
AutoResetEvent are = (AutoResetEvent)person.Wait;
Console.WriteLine("学生姓名是{0},年龄为{1}", person.Name, person.Age);
Thread.Sleep(3000);
are.Set();
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public WaitHandle Wait { get; set; }
}
public class ThreadExecutor
{
public static bool Execute(System.Threading.WaitCallback callback, object state)
{
try
{
return System.Threading.ThreadPool.QueueUserWorkItem((data) =>
{
try
{
callback(data);
}
catch (Exception ex)
{
//写日志
}
}, state);
}
catch (Exception e)
{
//写日志
}
return false;
}
public static bool Execute(System.Threading.WaitCallback callback)
{
try
{
return System.Threading.ThreadPool.QueueUserWorkItem((data) =>
{
try
{
callback(data);
}
catch (Exception ex)
{
//写日志
}
});
}
catch (Exception e)
{
//写日志
}
return false;
}
}
WaitHandle.WaitAll(),最大可监测64个WaitHandler ,如果你需要的多线程比较多,你可以分批,中间Sleep()一段时间,就可以了.
WaitHandle.WaitAny(),其中某一个线程完成后,就退出主线程.
4.6 委托类
使用CLR线程池中的工作者线程,最灵活最常用的方式就是使用委托的异步方法.委托包括下面3个重要方法:Invoke(),BeginInvoke(),EndInvoke()
当调用Invoke()方法时,对应此委托的所有方法都会被执行。而BeginInvoke与EndInvoke则支持委托方法的异步调用,由BeginInvoke启动的线程都属于CLR线程池中的工作者线程。
class Program
{
static void Main(string[] args)
{
SleepDelegate sleepDelegate = new SleepDelegate(Sleep);
IAsyncResult result = sleepDelegate.BeginInvoke("Sleep", null, null);
string data = sleepDelegate.EndInvoke(result);
Console.WriteLine(data);
Console.ReadKey();
}
delegate string SleepDelegate(object o);
//模拟执行长时间的任务
static string Sleep(object state)
{
string name = (string)state;
ThreadMessage(name);
Console.WriteLine("我还没有执行完呢,请耐心等待....");
Thread.Sleep(3000);
return "Hello" + name;
}
static void ThreadMessage(string data)
{
string message = string.Format("ThreadName is {0} ThreadId is:{1}",
data, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
}
class Program
{
static void Main(string[] args)
{
SleepDelegate sleepDelegate = new SleepDelegate(Sleep);
IAsyncResult result = sleepDelegate.BeginInvoke("Sleep", new AsyncCallback(Complete), "这里可以传个object值啊");
Console.ReadKey();
}
delegate string SleepDelegate(object o);
//模拟执行长时间的任务
static string Sleep(object state)
{
string name = (string)state;
ThreadMessage(name);
Console.WriteLine("我还没有执行完呢,请耐心等待....");
Thread.Sleep(3000);
return "Hello" + name;
}
static void Complete(IAsyncResult iresult)
{
AsyncResult result = (AsyncResult)iresult;
SleepDelegate sleepDelegate = (SleepDelegate)result.AsyncDelegate;
string obj = (string)result.AsyncState;
string data = sleepDelegate.EndInvoke(result);
Console.WriteLine(obj + data);
}
static void ThreadMessage(string data)
{
string message = string.Format("ThreadName is {0} ThreadId is:{1}",
data, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
}
ok,就写到这吧,委托异步处理的异常处理和等待所有线程完成,都可以参照4.4和4.5.
同时,在delegate.EndInvoke() 处也可以catch住异常.(这个在执行exe时候可看到效果,直接调试程序要报错)
这篇文章,是自己对多线程学习和总结吧. 对于多线程中的,线程安全,不是太了解,可能是下一步要探究的对象吧.
http://blog.csdn.net/wilsonke/article/details/7616984