.NET Thread、Task或Parallel实现多线程的使用总结

2023-08-30 08:33:08 浏览数 (2)

前言

多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

本文主要介绍.NET(C#) 中使用Thread、Task或Parallel实现多线程的总结,以及相关的示例代码。

一、Thread的使用

Thread是C#语言对线程对象的封装 ,从.NET 1.0版本就开始存在。

1、Thread初始化

代码语言:javascript复制
using System;
using System.Threading;
namespace ConsoleApplication
{
    class Program
    {
        private static void TaskFunc(string name)
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            thread.Name = name;
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
        }
        static void Main(string[] args)
        {
            Action action = () => TaskFunc("action");
            ThreadStart threadStart = () => TaskFunc("线程2");
            //Thread thread = new Thread(action);//不可以这样初始化
            Thread thread = new Thread(() => TaskFunc("线程1"));
            thread.Start();
            Thread thread2 = new Thread(threadStart);
            thread2.Start();
            Console.ReadKey();
        }
    }
}

2、Thread的常用操作

代码语言:javascript复制
using System;
using System.Threading;
namespace ConsoleApplication
{
    class Program
    {
        static bool isRun = false;
        private static void TaskFunc(string name)
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            thread.Name = name;
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
            while (isRun)
            {
                Thread.Sleep(500);
            }
        }
        static void Main(string[] args)
        {
            isRun = true;
            Thread thread = new Thread(() => TaskFunc("线程1"));
            //1、线程启动
            thread.Start();
            //thread.Suspend();//线程挂起_已弃用
            //thread.Resume();//唤醒线程_已弃用
            //2、线程销毁
            try
            {
                //thread.Abort();//销毁,方式是抛异常   不推荐使用
            }
            catch (Exception)
            {
                //Thread.ResetAbort();//取消Abort异常
            }
            //3、线程等待
            thread.Join(500);//最多等500
            Console.WriteLine("等待500ms");
            isRun = false;
            thread.Join();//当前线程等待thread完成
            //4、判断线程是否停止
            //while (thread.ThreadState != ThreadState.Stopped)
            //{
            //    Thread.Sleep(100);//当前线程 休息100ms  
            //}
            //5、设置后台线程
            //默认是前台线程,启动之后一定要完成任务的,阻止进程退出
            //thread.IsBackground = true;//指定后台线程:进程退出则退出
            //6、设置线程优先级  
            //thread.Priority = ThreadPriority.Highest;//线程优先级  
                                                 //CPU会优先执行 Highest   不代表说Highest就最先
            Console.ReadKey();
        }
    }
}

3、前台线程与后台线程的区别Thread默认是前台线程,启动之后一定要完成任务的,阻止进程退出,就是一定要线程运行完毕进程才会退出。而后台线程是进程的退出线程也退出。

4、Thread实现回调

代码语言:javascript复制
using System;
using System.Threading;
namespace ConsoleApplication
{
    class Program
    {
        private static void TaskFunc(string name)
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            thread.Name = name;
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
        }
        //启动子线程计算--完成委托后,该线程去执行后续回调委托
        private static void ThreadWithCallback(Action act, Action callback)
        {
            Thread thread = new Thread(() =>
            {
                act.Invoke();
                callback.Invoke();
            });
            thread.Start();
        }
        /// 
        /// 又要结果 要不阻塞 
        /// 
        /// 
        /// 
        /// 
        private static Func<T> ThreadWithReturn<T>(Func<T> func)
        {
            T t = default(T);
            Thread thread = new Thread(() =>
            {
                t = func.Invoke();
            });
            thread.Start();
            return () =>
            {
                thread.Join();
                return t;
            };
        }
        static void Main(string[] args)
        {
            Thread thread = new Thread(() => TaskFunc("线程1"));
            //不带返回值回调
            ThreadWithCallback(() => Console.WriteLine($"主线程执行  {Thread.CurrentThread.ManagedThreadId.ToString("00")}")
            , () => Console.WriteLine($"回调执行  {Thread.CurrentThread.ManagedThreadId.ToString("00")}"));
            //带返回值回调
            Func<DateTime> func = ThreadWithReturn(() =>
            {
                Thread.Sleep(1000);
                return DateTime.Now;
            });
            Console.WriteLine("返回值:"   func.Invoke());
            Console.ReadKey();
        }
    }
}

二、Task的使用

Task是.NET 4.0加入的,与线程池ThreadPool的功能类似,用Task开启新任务时,会从线程池中调用线程,而Thread每次实例化都会创建一个新的线程。

我们可以说Task是一种基于任务的编程模型。它与Thread的主要区别是,更加方便对线程进程调度和获取线程的执行结果。并且Task是针对多核有优化。

1、Task启动的方式

代码语言:javascript复制
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        private static void TaskFunc(string name)
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            //thread.Name = name;//只能设置一次
            Console.WriteLine(thread.Name);
            Console.WriteLine(name);
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
            //执行耗时间耗资源的任务
            Console.WriteLine(DateTime.Now.Ticks);
        }
        static void Main(string[] args)
        {
            //三种Task启动的方式
            var t1 = Task.Run(() => TaskFunc("线程1"));
            var t2 = Task.Factory.StartNew(() => TaskFunc("线程2"));
            var t3 = new Task(() => TaskFunc("线程3"));
            t3.Start();
            //启动带回调
            var t4 = Task.Run(() => TaskFunc("线程4")).ContinueWith(t => { Console.WriteLine(t.AsyncState); });
            Task.WaitAll(t1, t2, t3, t4);
            Console.ReadKey();
        }
    }
}

2、Thread.Sleep()和Task.Delay()的使用

代码语言:javascript复制
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        private static void TaskFunc(string name="")
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            //thread.Name = name;//只能设置一次
            Console.WriteLine(thread.Name);
            Console.WriteLine(name);
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
            //执行耗时间耗资源的任务
            Console.WriteLine(DateTime.Now.Ticks);
        }
        static void Main(string[] args)
        {
            //同步延时,阻塞主线程
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Thread.Sleep(500);
            stopwatch.Stop();
            Console.WriteLine("stopwatch:"   stopwatch.ElapsedMilliseconds);
            TaskFunc();
            //异步延时,不阻塞主线程
            Stopwatch stopwatch2 = new Stopwatch();
            stopwatch2.Start();
           var t1= Task.Delay(500).ContinueWith(t =>
            {
                stopwatch2.Stop();
                Console.WriteLine("stopwatch2:"   stopwatch2.ElapsedMilliseconds);
            });
            TaskFunc();
            //同步 异步延时,不阻塞主线程
            Stopwatch stopwatch3 = new Stopwatch();
            stopwatch3.Start();
            var t2=Task.Run(() =>
            {
                Thread.Sleep(500);
                stopwatch3.Stop();
                Console.WriteLine("stopwatch3:"   stopwatch3.ElapsedMilliseconds);
                TaskFunc();
            });
            Task.WaitAll(t1, t2);
        }
    }
}

3、通过判断线程状态来控制线程最大运行数

代码语言:javascript复制
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        private static void TaskFunc(string name = "")
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            //thread.Name = name;//只能设置一次
            Console.WriteLine(thread.Name);
            Console.WriteLine(name);
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
            //执行耗时间耗资源的任务
            Console.WriteLine(DateTime.Now.Ticks);
        }
        static void Main(string[] args)
        {
            var maxCount = 2;
            List<int> list = new List<int>();
            for (int i = 0; i < 100; i  )
            {
                list.Add(i);
            }
            Action<int> action = i =>
            {
                TaskFunc();
                Thread.Sleep(10);
            };
            List<Task> taskList = new List<Task>();
            foreach (var i in list)
            {
                int k = i;
                taskList.Add(Task.Run(() => action.Invoke(k)));
                if (taskList.Count > maxCount)
                {
                    Task.WaitAny(taskList.ToArray());
                    taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
                    Console.WriteLine("运行中的任务数:"   taskList.Count);
                }
            }
            //异步等待其全部执行完毕,不阻塞线程
            Task wTask = Task.WhenAll(taskList.ToArray());
            //wTask.ContinueWith()...
            //死等线程全部执行完毕,阻塞后面的线程
            Task.WaitAll(taskList.ToArray());
            //Task.WaitAll()和Task.WhenAll()区别一个阻塞线程,一个不阻塞
        }
    }
}

三、Parallel的使用

Parallel是并行编程,在Task的基础上做了封装,.NET FrameWork 4.5之后的版本可用,调用Parallel线程参与执行任务。

1、Parallel.For()和Parallel.ForEach()的使用

代码语言:javascript复制
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        private static void TaskFunc(string name = "")
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            //thread.Name = name;//只能设置一次
            Console.WriteLine(thread.Name);
            Console.WriteLine(name);
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
            //执行耗时间耗资源的任务
            Console.WriteLine(DateTime.Now.Ticks);
        }
        static void Main(string[] args)
        {
            Parallel.For(0, 5, i => { Console.WriteLine("i=" i); TaskFunc(); });
            Parallel.ForEach(new string[] { "0", "1", "2", "3", "4" }, j => { Console.WriteLine("j=" j); TaskFunc(); });
        }
    }
}

2、ParallelOptions 控制并发数量

代码语言:javascript复制
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        private static void TaskFunc(string name = "")
        {
            //获取正在运行的线程
            Thread thread = Thread.CurrentThread;
            //设置线程的名字
            //thread.Name = name;//只能设置一次
            Console.WriteLine(thread.Name);
            Console.WriteLine(name);
            //获取当前线程的唯一标识符
            int id = thread.ManagedThreadId;
            //获取当前线程的状态
            System.Threading.ThreadState state = thread.ThreadState;
            //获取当前线程的优先级
            ThreadPriority priority = thread.Priority;
            string strMsg = string.Format("Thread ID:{0}n"   "Thread Name:{1}n"  
                "Thread State:{2}n"   "Thread Priority:{3}n", id, thread.Name,
                state, priority);
            Console.WriteLine(strMsg);
            //执行耗时间耗资源的任务
            Console.WriteLine(DateTime.Now.Ticks);
        }
        static void Main(string[] args)
        {
            //state.Break()和state.Stop() 都不推荐用,异常情况处理较麻烦
            ParallelOptions parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = 2;//控制并发数量
            Parallel.For(1, 12, parallelOptions, (i, state) =>
            {
                //state.Stop();/*
                //调用 Stop 方法指示尚未开始的循环的任何迭代都无需运行。 它可以有效地取消循环的任何其他迭代。 但是,它不会停止已经开始执行的任何迭代。
                //调用 Stop 方法会导致此 IsStopped 属性返回到 true 仍在执行的循环的任何迭代。 这对于长时间运行的迭代特别有用,它可以检查 IsStopped 属性并在其值为时提前退出 true 。
                //Stop 通常在基于搜索的算法中使用,在找到结果后,不需要执行其他迭代。
                //state.Break();
                //Break 指示应运行当前迭代之后的任何迭代。 它可以有效地取消循环的任何其他迭代。 但是,它不会停止已经开始执行的任何迭代。 例如,如果 Break 是从从0到1000的并行循环的第100迭代调用的,则所有小于100的迭代仍应运行,但不会执行从101到1000的迭代。
                //对于可能已在执行的长时间运行的迭代, Break LowestBreakIteration 如果当前索引小于的当前值,则将属性设置为当前迭代的索引 LowestBreakIteration 。 若要停止其索引大于从争用执行的最低中断迭代的迭代,应执行以下操作:
                //检查属性是否 ShouldExitCurrentIteration 为 true 。
                //如果其索引大于属性值,则从迭代退出 LowestBreakIteration 。
                //说明如示例所示。
                //Break 通常在基于搜索的算法中采用,其中排序在数据源中存在。
                TaskFunc();
            });
        }
    }
}

0 人点赞