近期又看了一遍《C#高级编程》这本书,想对书中——任务、线程和同步这一章知识点做一个笔记,让以后工作中忘记某个知识点能直接拿来用,在此进行一个总结。
Parallel数据和任务并行
一、Parallel.For
1、用Parallel.For并行运行迭代
static void ParallelFor() { ParallelLoopResult result = Parallel.For(0, 10, item => { Console.WriteLine("{0},任务{1},线程{2}", item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); }); Console.WriteLine("完成:{0}", result.IsCompleted); }
2、利用ParallelLoopState的Break()方法提前停止For循环
static void ParallelForBreak() { ParallelLoopResult result = Parallel.For(0, 10, (item, pls) => { Console.WriteLine("{0},任务{1},线程{2}", item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); if (item > 5) pls.Break(); }); Console.WriteLine("完成:{0}", result.IsCompleted); Console.WriteLine("忽略:{0}", result.LowestBreakIteration); }
3、对每个线程进行初始化
static void ParallelForInit() { ParallelLoopResult result = Parallel.For<string>(0, 20, () => //Func<TLocal>仅对用于执行迭代的每个线程调用一次 { Console.WriteLine("初始化,任务:{0},线程:{1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId); return string.Format("线程:{0}", Thread.CurrentThread.ManagedThreadId); }, (item, pls, str1) => //Func<long, ParallelLoopState, TLocal, TLocal> 为循环体定义的委托 //第一个参数是循环迭代,第二个参数ParallelLoopState允许停止循环 //第三个参数接收初始化任务返回的值,类型是泛型For参数定义的 { Console.WriteLine("执行中,编号:{0},str1:{1},任务:{2},线程:{3}", item, str1, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); return string.Format("编号:{0}", item); }, (str1) => //Action<TLocal> 这个方法仅对于每个线程调用一次,这是一个线程退出方法 { Console.WriteLine("完成:{0}", str1); }); }
4、取消任务
/// <summary> /// 取消任务 /// </summary> static void ParallelCancel() { CancellationTokenSource cts = new CancellationTokenSource(); //注册一个任务取消完成时的委托 cts.Token.Register(() => { Console.WriteLine("任务已取消"); }); try { ParallelLoopResult result = Parallel.For(0, 10, new ParallelOptions() { CancellationToken = cts.Token }, item => { Console.WriteLine("循环:{0}", item); Thread.Sleep(1000); if (item == 5) cts.Cancel(); }); } catch (OperationCanceledException ex) { Console.WriteLine(ex.Message); } }
二、Parallel.ForEach
1、用Parallel.ForEach进行异步遍历
static void ParallelForeach() { string[] data = { "张三", "李四", "王五", "赵六", "钱七" }; ParallelLoopResult result = Parallel.ForEach(data, item => { Console.WriteLine("值:{0},任务:{1},线程:{2}", item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); }); }
2、利用ParallelLoopState的Break()方法中断ForEach遍历
static void ParallelForeachBreak() { string[] data = { "张三", "李四", "王五", "赵六", "钱七" }; ParallelLoopResult result = Parallel.ForEach(data, (item, pls, index) => { Console.WriteLine("值:{0},索引:{1},任务:{2},线程:{3}", item, index, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); if (item == "王五") pls.Break(); }); }
三、Parallel.Invoke
1、通过Parallel.Invoke()方法调用多个方法,如果多个任务应并行运行,就可以使用Parallel.Invoke方法,它提供了任务并行性模式
static void ParallelInvoke() { Parallel.Invoke(Zhangsan, Lisi); } static void Zhangsan() { Console.WriteLine("张三"); } static void Lisi() { Console.WriteLine("李四"); }
Task任务
一、创建一个任务执行的方法。
static object taskMethodLock = new object(); static void TaskMethod(object title) { lock (taskMethodLock) { Console.WriteLine(title); Console.WriteLine("任务:{0},线程:{1}", Task.CurrentId == null ? -1 : Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Console.WriteLine("线程池:{0}", Thread.CurrentThread.IsThreadPoolThread); Console.WriteLine("后台线程:{0}", Thread.CurrentThread.IsBackground); Console.WriteLine(); } }
二、用不同方式创建任务
/// <summary> /// 用不同方式创建任务 /// </summary> static void CreateTask() { //方式1:使用实例化的TaskFactory类 TaskFactory tf = new TaskFactory(); Task task = tf.StartNew(TaskMethod, "使用TaskFactory类开始任务"); //方式2:使用Task类的静态属性Factory Task.Factory.StartNew(TaskMethod, "使用Task类的静态属性Factory开始任务"); //方式3:使用Task类的构造函数 Task task1 = new Task(TaskMethod, "使用Task类的构造函数开始任务"); task1.Start(); }
三、创建同步任务
/// <summary> /// 同步任务 /// </summary> static void SyncTask() { TaskMethod("主线程"); Task task = new Task(TaskMethod, "同步任务"); task.RunSynchronously(); }
四、创建长时间运行的任务
/// <summary> /// 创建长时间运行的任务 /// </summary> static void LongRunTask() { //创建一个新的线程,而不是使用线程池中的线程 Task task = new Task(TaskMethod, "长时间运行任务", TaskCreationOptions.LongRunning); task.Start(); }
五、任务的结果
/// <summary> /// 任务的结果 /// </summary> static void TaskResult() { Task<string> task = new Task<string>((name) => { Console.WriteLine(name); return string.Format("我叫:{0}", name); }, "张三"); task.Start(); Console.WriteLine(task.Result); task.Wait(); }
六、连续任务
/// <summary> /// 连续任务 /// </summary> static void ContinueTask() { Task task1 = new Task(() => { Console.WriteLine("任务开始:{0}", Task.CurrentId); Thread.Sleep(3000); }); //task1结束后会立即执行task2 Task task2 = task1.ContinueWith((task) => { Console.WriteLine("完成任务:", task.Id); Console.WriteLine("当前任务:", Task.CurrentId); Console.WriteLine("执行一些清理工作"); Thread.Sleep(3000); }); task1.Start(); }
七、任务层次结构
/// <summary> /// 父子任务 /// </summary> static void ParentAndChildTask() { Task parent = new Task(ParentTask); parent.Start(); Thread.Sleep(2000); Console.WriteLine(parent.Status); Thread.Sleep(4000); Console.WriteLine(parent.Status); } static void ParentTask() { Console.WriteLine("任务编号:", Task.CurrentId); Task child = new Task(ChildTask); child.Start(); Thread.Sleep(1000); Console.WriteLine("开始子任务"); } static void ChildTask() { Console.WriteLine("子任务"); Thread.Sleep(5000); Console.WriteLine("子任务完成"); }
八、取消任务
//取消任务 static void CancelTask() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Token.Register(() => { Console.WriteLine("任务取消后执行"); }); Task task = new Task(() => { Console.WriteLine("开始任务"); for (int i = 0; i < 100; i++) { CancellationToken token = cts.Token; if (token.IsCancellationRequested) { Console.WriteLine("任务已取消"); token.ThrowIfCancellationRequested(); } Thread.Sleep(100); Console.WriteLine("循环编号:{0}", i); if (i == 5) cts.Cancel(); } }, cts.Token); task.Start(); try { task.Wait(); } catch (AggregateException ex) { Console.WriteLine("异常:{0},{1}", ex.GetType().Name, ex.Message); foreach (var innerException in ex.InnerExceptions) { Console.WriteLine("内部异常:{0},{1}", ex.InnerException.GetType().Name, ex.InnerException.Message); } } }
ThreadPool线程池
static void ThreadPoolDemo() { int workerThreads; int completionPortThreads; ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); Console.WriteLine("工作线程:{0},IO线程:{1}", workerThreads, completionPortThreads); for (int i = 0; i < 5; i++) { ThreadPool.QueueUserWorkItem(TaskMethod); } } static void TaskMethod(object state) { for (int i = 0; i < 3; i++) { Console.WriteLine("循环:{0},线程:{1}", i, Thread.CurrentThread.ManagedThreadId); } }
Thread类
一、创建一个线程
/// <summary> /// 创建一个线程 /// </summary> static void CreateThread() { Thread thread = new Thread(() => { Console.WriteLine("这是一个线程,{0}", Thread.CurrentThread.ManagedThreadId); }); thread.Start(); Console.WriteLine("这是一个主线程,{0}", Thread.CurrentThread.ManagedThreadId); }
二、给线程传递数据
1、方式1,使用带ParameterizedThreadStart委托参数的Thread构造函数
/// <summary> /// 给线程传递数据(方式1) /// </summary> static void ThreadWithData1() { var data = new { Message = "这是一条消息" }; Thread thread = new Thread((state) => { var obj = (dynamic)state; Console.WriteLine(obj.Message); }); thread.Start(data); }
2、方式2,创建一个自定义类,把线程的方法定义为实例方法,这样就可以初始化实例的数据之后,启动线程。
class MyThread { private string message = string.Empty; public MyThread(string message) { this.message = message; } public void ThreadMain() { Console.WriteLine(message); } }
/// <summary> /// 给线程传递数据(方式2) /// </summary> static void ThreadWithData2() { MyThread myThread = new MyThread("这是一条消息"); Thread thread = new Thread(myThread.ThreadMain); thread.Start(); }
三、后台线程
/// <summary> /// 后台线程 /// </summary> static void BackgroundThread() { Thread thread = new Thread(() => { Console.WriteLine("线程开始启动:{0}",Thread.CurrentThread.Name); Thread.Sleep(3000); Console.WriteLine("线程完成启动:{0}", Thread.CurrentThread.Name); }); thread.Name = "MyThread"; thread.IsBackground = true; thread.Start(); }
线程问题
一、争用条件
定义:如果两个或多个线程访问想通的对象,并且对共享状态的访问没有同步,就会出现争用条件。
下面的例子通过lock关键字解决”争用条件”问题
public class StateObject { private int state = 5; private object sync = new object(); public void ChangeState(int loop) { lock (sync) { if (state == 5) { state++; Trace.Assert(state == 6, "循环" + loop + "次后满足条件"); } state = 5; } } }
public class SampleTask { public void RaceCondition(object o) { Trace.Assert(o is StateObject, "参数类型必须是StateObject"); StateObject state = o as StateObject; int i = 0; while (true) { lock (state) { state.ChangeState(i++); } } } }
static void RaceConditions() { var state = new StateObject(); for (int i = 0; i < 2; i++) { Task.Factory.StartNew(() => { new SampleTask().RaceCondition(state); }); } }
二、死锁
定义:在死锁中,至少有两个线程被挂起,并等待对方解除锁定。由于两个线程都在等待对方,就出现了死锁,线程将无限等待下去。
private StateObject s1; private StateObject s2; public SampleTask(StateObject s1, StateObject s2) { this.s1 = s1; this.s2 = s2; } public void Deadlock1() { int i = 0; while (true) { lock (s1) { lock (s2) { s1.ChangeState(i); s2.ChangeState(i++); Console.WriteLine("运行{0}", i); } } } } public void Deadlock2() { int i = 0; while (true) { lock (s2) { lock (s1) { s1.ChangeState(i); s2.ChangeState(i++); Console.WriteLine("运行{0}", i); } } } }
static void DeadLock() { var state1 = new StateObject(); var state2 = new StateObject(); Task.Factory.StartNew(new SampleTask(state1, state2).Deadlock1); Task.Factory.StartNew(new SampleTask(state1, state2).Deadlock2); }
三、同步
1、SharedState类说明了如何使用线程之间的共享状态,并共享一个整数值。
2、将SharedState变量的State递增50000次
#region 方式一 public class ShareState { public int State { get; set; } } public class Job { ShareState shareState; public Job(ShareState shareState) { this.shareState = shareState; } public void DoTheJob() { for (int i = 0; i < 50000; i++) { lock (shareState) { shareState.State += 1; } } } } #endregion
#region 方式二 public class ShareState { private int state = 0; private object syncRoot = new object(); public int State { get { return state; } } public int IncrementState() { lock (syncRoot) { return ++state; } } } public class Job { ShareState shareState; public Job(ShareState shareState) { this.shareState = shareState; } public void DoTheJob() { for (int i = 0; i < 50000; i++) { lock (shareState) { shareState.IncrementState(); } } } } #endregion
#region 方式三:使用Interlocked类用于使变量的简单语句原子化 public class ShareState { private int state = 0; /// <summary> /// i++不是线程安全的,它的操作包括从内存中获取一个值,给该值递增1,再将它存储会内存。 /// 这些操作都可能会被线程调度器打断。 /// Interlocked类提供了以线程安全的方式递增、递减、交换和读取值的方式 /// </summary> public int State { get { return state; } } public int IncrementState() { return Interlocked.Increment(ref state); } } public class Job { ShareState shareState; public Job(ShareState shareState) { this.shareState = shareState; } public void DoTheJob() { for (int i = 0; i < 50000; i++) { shareState.IncrementState(); } } } #endregion
3、在Main方法中,创建一个SharedState对象,并把它传递给20个Task对象的构造函数。
class Program { static void Main(string[] args) { int numTasks = 20; var state = new ShareState(); var tasks = new Task[numTasks]; for (int i = 0; i < numTasks; i++) { tasks[i] = Task.Factory.StartNew(new Job(state).DoTheJob); } for (int i = 0; i < numTasks; i++) { tasks[i].Wait(); } Console.WriteLine("累计{0}", state.State); Console.ReadKey(); } }