多线程
目录--作者:hnZheng * 线程池以异步的方式执行一个方法 * 线程执行上下文 * 在异步线程间取消上下文的流动 * 协作式取消和超时 取消一个正在执行的线程 * System.Threading.Tasks任务的使用 * 等待任务完成并且获取结果 * 任务完成时启动新的任务 * 任务可以启动多个子任务 * 任务工厂的使用 * 任务调度器 常用 * Paraller的静态For 和ForEach 和 Invoke * 执行定时计算限制操作 定时程序 * 同步上下文任务调度器 // 线程池 异步执行一个方法 static void Main(string[] args) { #region 实例二 线程池以异步的方式执行一个方法 //实例一 线程池以异步的方式执行一个方法 Console.WriteLine("程序启动开始..."); ThreadPool.QueueUserWorkItem(FirstMethod); Console.WriteLine("执行其他操作..."); Thread.Sleep(10000);//其他工作执行10秒 Console.WriteLine("执行结束..."); Console.ReadLine(); #endregion } // 方法的签名要匹配 public static void FirstMethod(Object state) { Thread.Sleep(11000); Console.WriteLine("执行完FirstMethod..."); } //实例二 线程执行上下文 static void Main(string[] args) { #region 实例二 线程执行上下文 //实例二 线程执行上下文 // 1、将一些数据放在Main线程的逻辑调用上下文中,以下的线程池中加入三个线程 CallContext.LogicalSetData("Name", "zhenghaonan"); ThreadPool.QueueUserWorkItem(state => { Console.WriteLine("This is FirstMethod Name={0}", CallContext.LogicalGetData("Name")); }); //在异步线程间取消上下文的流动 ExecutionContext.SuppressFlow(); //这个线程中数据没有被流动下来 ThreadPool.QueueUserWorkItem(state => { Console.WriteLine("This is SecondMethod Name={0}", CallContext.LogicalGetData("Name")); }); // 在异步线程中恢复上下文的流动 ExecutionContext.RestoreFlow(); // 数据继续流动 ThreadPool.QueueUserWorkItem(state => { Console.WriteLine("This is ThridMethod Name={0}", CallContext.LogicalGetData("Name")); }); GetContextData(); Console.ReadLine(); // 以上实例 执行结果为 只有第二个线程中数据没有,其他的线程数据都有,并且四个线程是异步执行的 #endregion } public static void GetContextData() { Console.WriteLine("This is GetContextData Name={0}", CallContext.LogicalGetData("Name")); Console.WriteLine("This is GetContextData"); } //实例三 协作式取消和超时 取消一个正在执行的线程 static void Main(string[] args) { #region 实例三 协作式取消和超时 取消一个正在执行的线程 //实例三 协作式取消和超时 取消一个正在执行的线程 //创建第一个Token CancellationTokenSource cts = new CancellationTokenSource(); //创建第二个Token CancellationTokenSource cts1 = new CancellationTokenSource(); //创建一个线程1 ThreadPool.QueueUserWorkItem(o => { for (int i = 0; i < 50; i++) { Thread.Sleep(1000); Console.WriteLine("线程1---{0}", i); //获取Token,判断线程是否被取消 if (cts.Token.IsCancellationRequested) { //当线程取消操作是 Console.WriteLine("线程 1 已经被取消"); break; } } }); //创建一个线程2 ThreadPool.QueueUserWorkItem(o => { for (int i = 0; i < 50; i++) { Thread.Sleep(1000); Console.WriteLine("线程2--{0}", i); //获取Token,判断线程是否被取消 if (cts1.Token.IsCancellationRequested) { //当线程取消操作是 Console.WriteLine("线程 2 已经被取消"); break; } } }); //创建一个Token,关联一个或者多个Token,当cts1取消cts2也会被取消 //CancellationTokenSource cts2 = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token); CancellationTokenSource cts2 = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts.Token);// 关联多个Token,只要有一个取消,cts2就会被取消 //创建一个线程3 ThreadPool.QueueUserWorkItem(o => { for (int i = 0; i < 50; i++) { Thread.Sleep(1000); Console.WriteLine("线程3--{0}", i); //获取Token,判断线程是否被取消 if (cts2.Token.IsCancellationRequested) { //当线程取消操作是 Console.WriteLine("线程 3 已经被取消"); break; } } }); // 这里可以注册一个或者多个方法在线程取消时执行 //注册的三个方法也是异步执行的 //以下是线程1的Token,线程1取消时进行时的操作 cts.Token.Register(() => { Console.WriteLine("线程1取消时进行的操作0"); }); cts.Token.Register(() => { Console.WriteLine("线程1取消时进行的操作1"); }); cts.Token.Register(() => { Console.WriteLine("线程1取消时进行的操作2"); }); //以下是线程2的Token,线程1取消时进行时的操作 cts1.Token.Register(() => { Console.WriteLine("线程2取消时进行的操作0"); }); cts1.Token.Register(() => { Console.WriteLine("线程2取消时进行的操作1"); }); cts1.Token.Register(() => { Console.WriteLine("线程2取消时进行的操作2"); }); //以下是线程3的Token,线程1取消时进行时的操作 cts2.Token.Register(() => { Console.WriteLine("线程3取消时进行的操作0"); }); cts2.Token.Register(() => { Console.WriteLine("线程3取消时进行的操作1"); }); cts2.Token.Register(() => { Console.WriteLine("线程3取消时进行的操作2"); }); Console.WriteLine("线程将在10秒后取消"); //定时取消一个线程的执行,将于10秒后停止执行 //cts.CancelAfter(10000); Console.WriteLine("回车取消当前线程"); Console.ReadLine(); //立即取消一个线程的执行,取消线程1 cts.Cancel(); //立即取消一个线程的执行取消线程2 cts1.Cancel(); Console.ReadLine(); //ThreadPool的QueueUserWorkItem的异步操作,有许多限制,最大的问题就是没有内建的机制,你不知道这个线程什么时候完成, //也没有操作完成时的返回值,为了克服这些限制,使用System.Threading.Tasks中任务同样可以做异步造作. #endregion } //实例四 System.Threading.Tasks任务的使用 static void Main(string[] args) { #region System.Threading.Tasks任务的使用 //实例四 System.Threading.Tasks任务的使用 ThreadPool.QueueUserWorkItem(SecondMethod);// 使用QueueUserWorkItem,线程池 new Task(SecondMethod, "参数").Start();// 有参数,可以传递一个参数 new Task(GetContextData, TaskCreationOptions.None).Start();//无参数 Task.Factory.StartNew(GetContextData);// 创建并且启动线程(不带参数) Task.Factory.StartNew(SecondMethod, "");// 创建并且启动线程(带参数) Task.Run(() => { Console.WriteLine(" Task.Run() ..."); });//创建并且启动线程(不能带参数) Console.ReadLine(); #endregion } public static void SecondMethod(Object state) { Console.WriteLine("执行完FirstMethod..."); } public static void GetContextData() { Console.WriteLine("This is GetContextData"); } //等待任务完成并且获取结果 static void Main(string[] args) { #region 等待任务完成并且获取结果 // 有返回值,只能返回一个参数 Task<int> task = Task<int>.Factory.StartNew(x => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine("---{0}", i); } return 111; }, 100000); // 无返回值 Task task1 = Task.Factory.StartNew(x => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine("---{0}", i); } return 111; }, 100000); //wait()会阻塞线程,等待task完成 task.Wait(); //WaitAll()会阻塞线程,等待task,task1完成 Task.WaitAll(task, task1); //WaitAny()会阻塞线程,等待task,task1任意一个完成 Task.WaitAny(task, task1); // 只有task完成才能执行下面的方法 Console.WriteLine("等待执行完成,执行结果{0}", task.Result); Console.ReadLine(); #endregion } // 任务完成时启动新的任务 static void Main(string[] args) { #region 任务完成时启动新的任务 //使用wait等待任务完成的方式会阻塞线程,这样不但增加了系统资源的浪费,也不利于延展性和伸缩性 //以下的方式可以解决上面存在的问题 // 实例展示 // 创建一个新的任务 // 都是 针对 task是否完成或者异常 Task<int> task = Task.Run(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"{i}"); } Console.WriteLine("第一个任务开始:" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}"); return 10; }); //创建另一个新的任务,等待上面的任务完成后,将执行的结果展示出来 task.ContinueWith((x) => { Console.WriteLine("执行的结果为:" + $"{x.Result}" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}" + " 参数类型为:TaskContinuationOptions.None"); }, TaskContinuationOptions.None);// 默认 Console.ReadLine(); task.ContinueWith((x) => { Console.WriteLine("执行的结果为:" + $"{x.Result}" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}" + " 参数类型为:TaskContinuationOptions.OnlyOnRanToCompletion"); }, TaskContinuationOptions.OnlyOnRanToCompletion);//指示只有前面的任务完成 Console.ReadLine(); task.ContinueWith((x) => { Console.WriteLine("执行的结果为:" + $"{x.Result}" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}" + " 参数类型为:TaskContinuationOptions.NotOnRanToCompletion"); }, TaskContinuationOptions.NotOnRanToCompletion);//指示前面的任未完成 Console.ReadLine(); task.ContinueWith((x) => { Console.WriteLine("执行的结果为:" + $"{x.Result}" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}" + " 参数类型为:TaskContinuationOptions.OnlyOnCanceled"); }, TaskContinuationOptions.OnlyOnCanceled);//指示只有任务失败等价于 = OnlyOnFaulted||NotOnRanToCompletion Console.ReadLine(); task.ContinueWith((x) => { Console.WriteLine("执行的结果为:" + $"{x.Result}" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}" + " 参数类型为:TaskContinuationOptions.OnlyOnFaulted"); }, TaskContinuationOptions.OnlyOnFaulted);//之前任务执行失败 Console.ReadLine(); task.ContinueWith((x) => { Console.WriteLine("执行的结果为:" + $"{x.Result}" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}" + " 参数类型为:TaskContinuationOptions.ExecuteSynchronously"); }, TaskContinuationOptions.ExecuteSynchronously);//指示这个任务在第一个任务上完成 Console.ReadLine(); task.ContinueWith((x) => { Console.WriteLine("执行的结果为:" + $"{x.Result}" + $"当前线程的Id为:{Thread.CurrentThread.ManagedThreadId}" + " 参数类型为:TaskContinuationOptions.PreferFairness"); }, TaskContinuationOptions.PreferFairness);//指示希望这个任务尽快的执行 Console.ReadLine(); #endregion } //任务可以启动多个子任务 static void Main(string[] args) { #region 任务可以启动多个子任务 // 实例 // 创建一个父任务 Task<int[]> parentTasks = new Task<int[]>(() => { //定义一个子任务子任务返回结果的数组,其中子任务都是异步执行 int[] result = new int[3]; //创建三个子任务,并且关联父任务 new Task(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"任务一{i}"); } result[0] = 1; }, TaskCreationOptions.AttachedToParent).Start(); new Task(() => { for (int i = 0; i < 5; i++) { Thread.Sleep(1000); Console.WriteLine($"任务二{i}"); } result[1] = 2; }, TaskCreationOptions.AttachedToParent).Start(); new Task(() => { for (int i = 0; i < 2; i++) { Thread.Sleep(1000); Console.WriteLine($"任务三{i}"); } result[2] = 3; }, TaskCreationOptions.AttachedToParent).Start(); return result; }); //父任务完成之后子任务的返回结果 parentTasks.ContinueWith((x) => { //遍历结果 Array.ForEach(parentTasks.Result, (s) => { Console.WriteLine($"结果为{s}"); }); }); // 启动所有的子任务 parentTasks.Start(); Console.ReadLine(); #endregion } //任务工厂的使用 static void Main(string[] args) { #region 任务工厂的使用 //实例 // 创建一个相同配置的工厂对象 CancellationTokenSource cts = new CancellationTokenSource(); //创建一个统一配置的的任务工厂有返回值 TaskFactory<int> tf = new TaskFactory<int>( cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default ); //创建三个子任务 var childrensTask = new[] { tf.StartNew(() => { Console.WriteLine("子任务一"); return 1; },cts.Token), tf.StartNew(() => { Console.WriteLine("子任务一"); return 55; }), tf.StartNew(() => { Console.WriteLine("子任三"); return 2; }) }; //只要有一个任务执行失败,立即取消其他的所有任务 for (int task = 0; task < childrensTask.Length; task++) { childrensTask[task].ContinueWith((x) => { cts.Cancel(); }, TaskContinuationOptions.OnlyOnFaulted); } // 当所有的任务都完成 tf.ContinueWhenAll(childrensTask, t => { return childrensTask.Where(x => !x.IsCanceled || !x.IsFaulted).Max(tt => tt.Result); }, CancellationToken.None).ContinueWith(h => { Console.WriteLine($"最大值为{h.Result}"); }); Console.ReadLine(); #endregion } //任务调度器 常用 #region 任务调度器 常用 //实例 //任务调度器非常灵活 //默认情况下FcL提供了两个任务调度器 //1 线程池任务调度器 (thread pool task scheduler) //2 同步上下文任务调度器(synchroizition context task scheduler) //所有的应用程序都是有的是线程池任务调度器 //带界面的调度池使用上下文调度器,比如Winforms WPF TaskScheduler taskScheduler = TaskScheduler.Default; //以下任务调度器只允许不超过N个任务同时执行 LimitedConcurrencyLevelTaskScheduler limitedConcurrencyLevelTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(2);//只允许2个任务 //以下四个任务 两两执行 Task.Factory.StartNew(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"任务一{i}"); } }, CancellationToken.None, TaskCreationOptions.None, limitedConcurrencyLevelTaskScheduler); Task.Factory.StartNew(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"任务二{i}"); } }, CancellationToken.None, TaskCreationOptions.None, limitedConcurrencyLevelTaskScheduler); Task.Factory.StartNew(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"任务三{i}"); } }, CancellationToken.None, TaskCreationOptions.None, limitedConcurrencyLevelTaskScheduler); Task.Factory.StartNew(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"任务四{i}"); } }, CancellationToken.None, TaskCreationOptions.None, limitedConcurrencyLevelTaskScheduler); Console.ReadLine(); Console.WriteLine($"{limitedConcurrencyLevelTaskScheduler.CurrentCount}"); Console.ReadLine(); #endregion // 限制线程数的调度器 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace 多线程的学习 { public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler { /// <summary>Whether the current thread is processing work items.</summary> [ThreadStatic] private static bool _currentThreadIsProcessingItems; /// <summary>The list of tasks to be executed.</summary> private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks) /// <summary>The maximum concurrency level allowed by this scheduler.</summary> private readonly int _maxDegreeOfParallelism; /// <summary>Whether the scheduler is currently processing work items.</summary> private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks) /// <summary> /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the /// specified degree of parallelism. /// </summary> /// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param> public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism) { if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism"); _maxDegreeOfParallelism = maxDegreeOfParallelism; } /// <summary> /// current executing number; /// </summary> public int CurrentCount { get; set; } /// <summary>Queues a task to the scheduler.</summary> /// <param name="task">The task to be queued.</param> protected sealed override void QueueTask(Task task) { // Add the task to the list of tasks to be processed. If there aren't enough // delegates currently queued or running to process tasks, schedule another. lock (_tasks) { Console.WriteLine("Task Count : {0} ", _tasks.Count); _tasks.AddLast(task); if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism) { ++_delegatesQueuedOrRunning; NotifyThreadPoolOfPendingWork(); } } } int executingCount = 0; private static object executeLock = new object(); /// <summary> /// Informs the ThreadPool that there's work to be executed for this scheduler. /// </summary> private void NotifyThreadPoolOfPendingWork() { ThreadPool.UnsafeQueueUserWorkItem(_ => { // Note that the current thread is now processing work items. // This is necessary to enable inlining of tasks into this thread. _currentThreadIsProcessingItems = true; try { // Process all available items in the queue. while (true) { Task item; lock (_tasks) { // When there are no more items to be processed, // note that we're done processing, and get out. if (_tasks.Count == 0) { --_delegatesQueuedOrRunning; break; } // Get the next item from the queue item = _tasks.First.Value; _tasks.RemoveFirst(); } // Execute the task we pulled out of the queue base.TryExecuteTask(item); } } // We're done processing items on the current thread finally { _currentThreadIsProcessingItems = false; } }, null); } /// <summary>Attempts to execute the specified task on the current thread.</summary> /// <param name="task">The task to be executed.</param> /// <param name="taskWasPreviouslyQueued"></param> /// <returns>Whether the task could be executed on the current thread.</returns> protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { // If this thread isn't already processing a task, we don't support inlining if (!_currentThreadIsProcessingItems) return false; // If the task was previously queued, remove it from the queue if (taskWasPreviouslyQueued) TryDequeue(task); // Try to run the task. return base.TryExecuteTask(task); } /// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary> /// <param name="task">The task to be removed.</param> /// <returns>Whether the task could be found and removed.</returns> protected sealed override bool TryDequeue(Task task) { lock (_tasks) return _tasks.Remove(task); } /// <summary>Gets the maximum concurrency level supported by this scheduler.</summary> public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } } /// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary> /// <returns>An enumerable of the tasks currently scheduled.</returns> protected sealed override IEnumerable<Task> GetScheduledTasks() { bool lockTaken = false; try { Monitor.TryEnter(_tasks, ref lockTaken); if (lockTaken) return _tasks.ToArray(); else throw new NotSupportedException(); } finally { if (lockTaken) Monitor.Exit(_tasks); } } } } //Paraller的静态For 和ForEach 和 Invoke static void Main(string[] args) { #region Paraller的静态For 和ForEach 和 Invoke Task.Factory.StartNew(() => { //进程下线程 while (true) { Thread.Sleep(1000); Process current = Process.GetCurrentProcess(); ProcessThreadCollection allThreads = current.Threads; Console.WriteLine($"活动总线程{allThreads.Count}"); } }); //这个地方创建100000个线程Cpu使用率为100% //集合较小的可以使用这个 //MaxDegreeOfParallelism 这个参数可以限制最大并发数目 Parallel.For(0, 100000, new ParallelOptions() { MaxDegreeOfParallelism = 10, CancellationToken = new CancellationToken() }, i => { Thread.Sleep(5000); Console.WriteLine($"当前线程{Thread.CurrentThread.ManagedThreadId}"); }); int[] s = new int[] { 1, 2, 3 }; Parallel.ForEach(s, new ParallelOptions { MaxDegreeOfParallelism = 10 }, x => { }); //并行执行不同的方法 Parallel.Invoke(new ParallelOptions() { MaxDegreeOfParallelism = 10 }, () => { }, () => { }, //.... () => { } ); Console.ReadLine(); #endregion } //执行定时计算限制操作 定时程序 static void Main(string[] args) { #region 执行定时计算限制操作 定时程序 _time = new Timer(ChangeStstus, null, Timeout.Infinite, Timeout.Infinite); // 设置为Timeout.Infinite 等待ChangeStstus方法完成在开始下一次回调 _time.Change(0, Timeout.Infinite); // 以下不会等待ChangeStstus方法完成,就会开始第二次,一般不采用 // _time.Change(0, 2000); // 除了以上方法我们还可以使用线程来做定时器 ChnangeStatus(); Console.WriteLine($"将在两秒后继续开始"); Console.ReadLine(); #endregion } //声明一个Time对象 public static Timer _time; //这个方法签名必须和Callback的签名保持一致 public static void ChangeStstus(Object state) { Console.WriteLine($"将在两秒后继续开始"); // Do other work for (int i = 0; i < 10; i++) { Thread.Sleep(1000); Console.WriteLine($"{i}"); } //将在2秒后继续触发回调方法,这里不写不会再触发,一定要保证让Time对象处于存活的状态 _time.Change(2000, Timeout.Infinite); } // 线程做定时 public static async void ChnangeStatus() { while(true) { for (int i = 0; i < 10; i++) { Thread.Sleep(2000); Console.WriteLine($"执行次数中{i}"); } //在不会阻塞线程的前提下延迟2秒,在while循环中2秒执行一次,会等待上面的work完成,2秒后开始下一次 await Task.Delay(2000); } } //获取同步上下文任务调度器 //带界面的调度池使用上下文调度器,比如Winforms WPF using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace 同步上下文任务调度器 { public partial class Form1 : Form { public Form1() { InitializeComponent(); //获取同步上下文任务调度器 taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } TaskScheduler taskScheduler; private void button1_Click(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { Task.Factory.StartNew(() => { //这里不是用同步上下文调度池,需要用委托来更新ui线程 this.Invoke(new Action(() => { button1.Text = "线程一"; })); }, CancellationToken.None); } private void button3_Click(object sender, EventArgs e) { //TaskFactory tf = new TaskFactory( // CancellationToken.None, // TaskCreationOptions.PreferFairness, // TaskContinuationOptions.ExecuteSynchronously, // taskScheduler // ); //tf.StartNew(() => { // button1.Text = "线程er"; //}); // 使用调度池,直接在ui线程上更新 Task.Factory.StartNew(() => { button1.Text = "线程er"; }, CancellationToken.None, TaskCreationOptions.None, taskScheduler); } } }