线程池和任务工厂实现多线程异步运行

将多个线程在线程池中运行

知识点保温

引用一下微软官网的介绍:

线程池: https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.threadpool?redirectedfrom=MSDN&view=netframework-4.7.2

 


直接上Demo

多线程运行 VS 单线程运行

    /// <summary>
    /// 高效多线程测试
    /// </summary>
    public class ThreadTestController : ApiController
    {
        static AutoResetEvent autoResetEvent = new AutoResetEvent(false);
        private static string keySet = System.Configuration.ConfigurationManager.AppSettings["KeySetting"];

        /// <summary>
        /// 多线程运行 -- 线程池
        /// </summary>
        /// <returns></returns>
        public ApiReponseBaseModel MultThreads()
        {
            ApiReponseBaseModel result = new ApiReponseBaseModel();
            List<string> list = new List<string>() { "", "", "", "", "", "", "", "" };
            ApiReponseBaseModel masterVal = new ApiReponseBaseModel() { Id = 1, Success = false, Message = "one" };
            List<string> newList = new List<string>();
            try
            {
                int i = 0;
                foreach (var item in list)
                {
                    try
                    {
                        if (item == "") throw new Exception("人造异常");
                        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate (object obj)
                        {
                            if (string.IsNullOrEmpty(item)) { i++; newList.Add((i).ToString() + ":" + "为空了"); return; }  //保证每个子线程都有执行 i++
                            try
                            {
                                #region 
                                ApiReponseBaseModel val = new ApiReponseBaseModel
                                {
                                    Id = masterVal.Id,
                                    Success = masterVal.Success,
                                    Message = masterVal.Message
                                };
                                #endregion
                                val.Message = item;

                                Thread.Sleep(500);
                                lock (newList)
                                {
                                    newList.Add(i.ToString() + ":" + val.Message);
                                }
                            }
                            catch (Exception ex)
                            {
                                newList.Add((i+1).ToString() + ":" + ex.Message);
                            }
                            finally
                            {
                                if (++i == list.Count) autoResetEvent.Set();
                            }
                            
                        }), null);
                    }
                    catch (Exception ex)
                    {
                        i++;    //发生异常,一定要记得 i++
                        newList.Add(i.ToString() + ":" + ex.Message);
                        continue;
                    }
                    //finally  //【误区】千千万万不要在这里写逻辑判断(i == list.Count 或者写 i++),这个finally是主线程的,不一定会在子线程之后执行的
                    //{
                    //    i++;
                    //    if (i == list.Count) autoResetEvent.Set();
                    //}
                }

                autoResetEvent.WaitOne();

                newList.Add("the end");
                result.Data = newList;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            
            return result;
        }

        /// <summary>
        /// 单线程运行
        /// </summary>
        /// <returns></returns>
        public ApiReponseBaseModel SingleThread()
        {
            ApiReponseBaseModel result = new ApiReponseBaseModel();
            List<string> list = new List<string>() { "", "", "", "", "", "", "", "" };
            ApiReponseBaseModel masterVal = new ApiReponseBaseModel() { Id = 1, Success = false, Message = "one" };
            List<string> newList = new List<string>();
            int i = 0;
            foreach (var item in list)
            {
                if (string.IsNullOrEmpty(item)) { i++; newList.Add((i).ToString() + ":" + "为空了"); continue; }
                #region 
                ApiReponseBaseModel val = new ApiReponseBaseModel
                {
                    Id = masterVal.Id,
                    Success = masterVal.Success,
                    Message = masterVal.Message
                };
                #endregion
                val.Message = item;
                Thread.Sleep(500);
                newList.Add(i.ToString() + ":" + val.Message);
                i++;
            }
            newList.Add("the end");
            result.Data = newList;
            return result;
        }

    }

 

结果:

 

 

解析:

1、以上两个方法的运行,我们都在方法中休眠了,用以突显出多线程异步执行达到运行更快的效果。

2、在第一个方法(多线程运行 -线程池)中,用到了线程池,其关键在于 AutoResetEvent 的使用,它的.Set() 和 WaitOne() 是关键点。

      WaitOne() 会阻塞当前的线程,而等当前 WaitHandle 发来的信息,Set() 方法就是发出信号的。

3、那么很重要的地方来了,就是怎么在逻辑中表达,所需要计算的数据和执行的进程都已经执行完了,而在恰当的地方适当的时候发起信号,激活主线程。

4、以上多线程方法中,我们还用到了个 lock 锁,这也是极其微妙关键的。我们的多个子线程运行的程序模块中,都有访问了一个共同的对象 newList。newList 是主线程的一个 引用类型变量,为了防止多个线程在同一个时间点对这个变量进行更改操作(Add),

     而引发的异常错误,我们在做这类操作的时候,给这个共同的资源(newList)加上锁,让这样的操作一个接着一个来。

     进程中的 val 这个变量,变量类型是(ApiReponseBaseModel),也是个引用类型。我们不在子进程中直接用主线程的 masterVal 变量,也是这个原因,防止多个子线程同时对共同资源做修改操作。

5、WaitHandler 类的小图解

 

 


 

我们再试个任务工厂的Demo

知识点保温

任务工厂的知识保温链接:https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskfactory?view=netframework-4.7.2

需要温习任务工厂的小伙伴们可以进入以上链接,吸食日夜精华


 

直接上菜

        /// <summary>
        /// 多线程运行 -- 任务工厂
        /// </summary>
        /// <returns></returns>
        public ApiReponseBaseModel TaskFactory()
        {
            ApiReponseBaseModel result = new ApiReponseBaseModel();
            List<string> list = new List<string>() { "", "", "", "", "", "", "", "" };
            ApiReponseBaseModel masterVal = new ApiReponseBaseModel() { Id = 1, Success = false, Message = "one" };
            List<string> newList = new List<string>();

            List<Task> taskList = new List<Task>();
            int i = 0;
            list.ForEach(p =>
            {
                taskList.Add(Task.Factory.StartNew(() =>
                {
                    if (string.IsNullOrEmpty(p)) { i++; newList.Add((i).ToString() + ":" + "为空了"); return; }  //保证每个子线程都有执行 i++
                    try
                    {
                        if (p == "") throw new Exception("人造异常");
                        #region 
                        ApiReponseBaseModel val = new ApiReponseBaseModel
                        {
                            Id = masterVal.Id,
                            Success = masterVal.Success,
                            Message = masterVal.Message
                        };
                        #endregion
                        val.Message = p;

                        Thread.Sleep(500);
                        lock (newList)
                        {
                            newList.Add(i.ToString() + ":" + val.Message);
                        }
                    }
                    catch (Exception ex)
                    {
                        newList.Add((i + 1).ToString() + ":" + ex.Message);
                    }
                }));

                //taskList.Add(Task.Factory.StartNew(fn =>
                //{

                //}, null));
            });
            
            Task finalTask = Task.Factory.ContinueWhenAll(taskList.ToArray(), fn => {
                newList.Add("finallyTask the end");
            });
            finalTask.Wait();
            newList.Add("main thread the end");
            result.Data = newList;
            return result;
        }

 

效果:

 

解析:

1、我们在任务工厂里边搞了几个任务,每个任务我们都休眠,模仿长时间运行效果。

2、任务工厂的关键在于 ContinueWhenAll() 方法。顾名思义,当该方法参数所指的任务都执行的时候,执行这一方法,这个方法返回一个任务,我们定义一个任务变量(finalTask)来接收。

     紧接着,我们放这个变量(finalTask)进行等待,finalTask.Wait()。

 


 

小总结

 通过以上三个方法的比较

1、多线程比单线程是有优势的,通常情况下运行时间是会更快的,除非服务器爆了

2、在易于编写使用的角度来说,个人觉得任务工厂的办法会比线程池的方法好一些。

3、多读书,多敲码。            ----- 致爱码仕 & 牧码人

 

 


 

 

author:韦小明

本文路径:http://www.cnblogs.com/youler/p/9845473.html

 


 

posted @ 2018-10-24 19:00  蓝色咖啡屋  阅读(579)  评论(0编辑  收藏  举报
韦小明(email:3301526363@qq.com)