南皮网站建设,腾讯云建站平台,响应式网站制设计,公司网站做的好的公司C#多线程#xff08;补充#xff09; C# 多线程的补充在C#中使用多线程1. Thread类2. 线程池3. Parallel类4. Task类启动任务接收任务的返回值同步调用指定连续任务任务的层次结构 5. BackgroundWorker控件 C# 多线程的补充
在C#中使用多线程
1. Thread类
使用Thread类通过… C#多线程补充 C# 多线程的补充在C#中使用多线程1. Thread类2. 线程池3. Parallel类4. Task类启动任务接收任务的返回值同步调用指定连续任务任务的层次结构 5. BackgroundWorker控件 C# 多线程的补充
在C#中使用多线程
1. Thread类
使用Thread类通过ThreadStart无参数或ParameterizedThreadStart一个输入参数类型的委托创建一个Thread对象开启一个新线程执行该委托传递的任务此时线程尚未处于运行状态。调用Start()函数启动线程当前线程继续执行。调用Join()函数可以阻塞当前线程直到调用Join()的线程终止。
Thread类创建的线程默认为前台线程可以通过IsBackground属性设置其为前台或后台线程。还可以通过Priority属性设置线程的优先级。
如需中止线程调用Abort()方法在调用该方法的线程上抛出ThreadAbortException异常以结束该线程。线程内部可以通过try catch捕获该异常在catch模块中进行一些必要的处理如释放持有的锁和文件资源等还可以通过Thread.ResetAbort()方法阻止线程的中止。但是通常来说应当慎重使用Abort()方法如果在当前线程中抛出该异常其结果是可预测的但是对于其他线程它会中断任何正在执行的代码有可能中断静态对象的生成造成不可预测的结果。
using System;
using System.Threading;namespace ConsoleApplication1
{public class ThreadExample{public static void Main(){Thread thread new Thread(new ThreadStart(DoWork));thread.Start();//thread.Join();Thread.Sleep(10);thread.Abort();Thread parameterizedThread new Thread(new ParameterizedThreadStart(DoWorkWithParam));parameterizedThread.Start(test);Console.ReadKey();}public static void DoWork(){try{for (int i 0; i 10000; i)Console.WriteLine(Work thread: i.ToString());}catch (Exception e){Console.WriteLine(e.Message);Thread.ResetAbort();}Console.WriteLine(Work thread: still alive and working.);Thread.Sleep(1000);Console.WriteLine(Work thread: finished working.);}public static void DoWorkWithParam(object obj){string msg (string)obj;Console.WriteLine(Parameterized Work thread: msg);}}
}2. 线程池
ThreadPool类维护一个线程的列表提供给用户以执行不同的小任务减少频繁创建线程的开销。ThreadPool的使用比较简单只需调用ThreadPool.QueueUserWorkItem()方法传递一个WaitCallback类型的委托线程池即从池中选择一个线程执行该任务。 public static void Main(){for (int i 0; i 5; i)ThreadPool.QueueUserWorkItem(DoWork);Console.ReadKey();}public static void DoWork(Object o){ for (int i 0; i 3; i)Console.WriteLine(loop:{0}, thread id: {1}, i, Thread.CurrentThread.ManagedThreadId);}但是线程池的使用也有一些限制
线程池中的线程均为后台线程并且不能修改为前台线程不能给入池的线程设置优先级或名称对于COM对象入池的所有线程都是多线程单元MTA线程许多COM对象都需要单线程单元STA 线程入池的线程只适合时间较短的任务如果线程需要长时间运行应使用Thread类创建线程或使用Task的LongRunning选项
3. Parallel类
Parallel和Task类都位于System.Threading.Task命名空间中是对Thread和ThreadPool类更高级的抽象。Parrallel类有For()、ForEach()、Invoke()三个方法前两者在每次迭代中调用相同的代码实现了数据并行性Invoke()允许同时调用不同的方法实现任务并行性。
For()和ForEach()两者的用法类似。如下例调用Parallel.For()方法实现从0到10的迭代每次迭代是并行执行的并且从输出结果可以看出执行顺序是不能保证的。
public static void Main(){ParallelLoopResult result Parallel.For(0, 10, i {Console.WriteLine(i:{0}, thread id: {1}, i, Thread.CurrentThread.ManagedThreadId);Thread.Sleep(10);});Console.WriteLine(Is completed: {0}, result.IsCompleted);//i: 0, thread id: 9//i: 2, thread id: 10//i: 1, thread id: 9//i: 3, thread id: 10//i: 4, thread id: 9//i: 6, thread id: 11//i: 7, thread id: 10//i: 5, thread id: 9//i: 8, thread id: 12//i: 9, thread id: 11//Is completed: TrueConsole.ReadKey();}通过ParallelLoopState的Break()或Stop()方法可以提前中断Parallel.For()的迭代。
public static void Main(){ParallelLoopResult result Parallel.For(0, 100, (i, state) {Console.WriteLine(i:{0}, thread id: {1}, i, Thread.CurrentThread.ManagedThreadId);if (i 10)state.Break();Thread.Sleep(10);});Console.WriteLine(Is completed: {0}, result.IsCompleted);Console.WriteLine(Lowest break iteration: {0}, result.LowestBreakIteration);//i: 0, thread id: 10//i: 25, thread id: 6//i: 1, thread id: 10//i: 2, thread id: 10//i: 3, thread id: 10//i: 4, thread id: 10//i: 5, thread id: 10//i: 6, thread id: 10//i: 7, thread id: 10//i: 8, thread id: 10//i: 9, thread id: 10//i: 10, thread id: 10//i: 11, thread id: 10//Is completed: False//Lowest break iteration: 11Console.ReadKey();}如需同时执行多个不同的任务可以使用Parallel.Invoke()方法它允许传递一个Action委托数组。 public static void Main(){Parallel.Invoke(Func1, Func2, Func3);Console.ReadKey();}4. Task类
相比于Thread类Task类为控制线程提供了更大的灵活性。Task类可以获取线程的返回值也可以定义连续的任务——在一个任务结束结束后开启下一个任务还可以在层次结构中安排任务在父任务中可以创建子任务这样就创建了一种依赖关系如果父任务被取消子任务也随之取消。Task类默认使用线程池中的线程如果该任务需长期运行应使用TaskCreationOptions.LongRunning属性告诉任务管理器创建一个新的线程而不是使用线程池中的线程。
启动任务
以下程序演示了几种通过Task类启动任务的方式 public class ThreadExample{public static void Main(){TaskFactory tf new TaskFactory();Task t1 tf.StartNew(TaskMethod.DoTask, using a task factory);Task t2 Task.Factory.StartNew(TaskMethod.DoTask, factory via a task);Task t3 new Task(TaskMethod.DoTask, using a task constructor and start);t3.Start();//需要.NetFramework 4.5以上var t4 Task.Run(() TaskMethod.DoTask(using Run method));Console.ReadKey();}class TaskMethod{static object taskLock new object();public static void DoTask(object msg){lock (taskLock){Console.WriteLine(msg);Console.WriteLine(Task id:{0}, Thread id :{1},Task.CurrentId null ? no task : Task.CurrentId.ToString(),Thread.CurrentThread.ManagedThreadId);}}}接收任务的返回值
对于任务有返回值的情况可使用Task泛型类TResult定义了返回值的类型以下代码演示了调用返回int值的任务的方法。 public static void Main(){var t5 new Taskint(TaskWithResult, Tuple.Createint, int(1, 2));t5.Start();t5.Wait();Console.WriteLine(adder results: {0}, t5.Result);Console.ReadKey(); }public static int TaskWithResult(object o){Tupleint, int adder (Tupleint, int)o;return adder.Item1 adder.Item2;}同步调用
调用Task类的RunSynchronously()方法可以实现同步调用直接在当前线程上调用该任务。
public static void Main(){TaskMethod.DoTask(Just Main thread);Task t1 new Task(TaskMethod.DoTask, using Run Sync);t1.RunSynchronously();//输出结果//Just Main thread//Task id: no task, Thread id: 9////using Run Sync//Task id:1, Thread id :9}指定连续任务
调用Task类的ContinueWith()方法可以指定连续的任务。
public static void Main(){TaskFactory tf new TaskFactory();Task t1 tf.StartNew((){Console.WriteLine(Current Task id {0}, Task.CurrentId);Console.WriteLine(执行任务1\r\n);Thread.Sleep(10);});Task t2 t1.ContinueWith((t) {Console.WriteLine(Last Task id {0}, t.Id);Console.WriteLine(Current Task id {0}, Task.CurrentId);Console.WriteLine(执行任务2\r\n);Thread.Sleep(10);});Task t3 t2.ContinueWith(delegate(Task t) {Console.WriteLine(Last Task id {0}, t.Id);Console.WriteLine(Current Task id {0}, Task.CurrentId);Console.WriteLine(执行任务3\r\n);}, TaskContinuationOptions.OnlyOnRanToCompletion);Console.ReadKey(); }//执行结果////Current Task id 1//执行任务1//Last Task id 1//Current Task id 2//执行任务2//Last Task id 2//Current Task id 3//执行任务3从执行结果可以看出任务123被顺序执行同时通过 TaskContinuationOptions 还可以指定何种情况下继续执行该任务常用的值包括OnlyOnFaulted, OnlyOnCanceled, NotOnFaulted, NotOnCanceled等。如将上例中的OnlyOnRanToCompletion改为OnlyOnFaulted任务2结束之后任务3将不被执行。
对于ContinueWith()的使用MSDN演示了更加优雅的“流式”调用方法
private void Button1_Click(object sender, EventArgs e)
{ var backgroundScheduler TaskScheduler.Default; var uiScheduler TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(delegate { DoBackgroundComputation(); }, backgroundScheduler). ContinueWith(delegate { UpdateUI(); }, uiScheduler). ContinueWith(delegate { DoAnotherBackgroundComputation(); }, backgroundScheduler). ContinueWith(delegate { UpdateUIAgain(); }, uiScheduler);
} 任务的层次结构
如果在一个Task内部创建了另一个任务这两者间就存在父/子的层次结构当父任务被取消时子任务也会被取消。如果不希望使用该层次结构可在创建子任务时选择TaskCreationOptions.DetachedFromParent。
5. BackgroundWorker控件
除了上述四类直接操作多线程的方法C#还提供了BackgroundWorker控件帮助用户更简单、安全地实现多线程运算。该控件提供了DoWork, ProgressChanged 和 RunWorkerCompleted事件为DoWork添加事件处理函数再调用RunWorkerAsync()方法即可创建一个新的线程执行DoWork任务。ProgressChanged和RunWorkerCompleted事件均在UI线程中执行添加相应的处理函数即可完成任务线程与UI线程间的交互可用于显示任务的执行状态完成百分比、执行结果等。同时该控件还提供了CancleAsync()方法以中断线程的执行需注意的是调用该方法后只是将控件的CancellationPending属性置True用户需在程序执行过程中查询该属性以判定是否应中断线程。