• 多线程实践


         一直对多线程不感冒,常见的场景下用不到这种技术,所以一直不愿去了解,但是遇到一些耗时的任务时就要考虑了。下面的例子是项目中遇到的,不想说这个如何的高深,只想说我也不是很了解到底如何工作的,到底带来了多少的效率提升。书上的理论要多枯燥有多枯燥,枯燥的我都不想去动手写里面的例子,下面的例子是实际用到的,还有些意思,不管怎么说开个头吧。

      1.ManualResetEvent[] doEvents = new ManualResetEvent[threadCount];
      通知一个或多个正在等待的线程已发生事件。 谁通知谁,发生的事情是指?一头雾水

      2.ThreadPool.QueueUserWorkItem(new WaitCallback(DealData), new object[] { i, doEvents[i] });
      
    线程池中创建一个线程池线程来执行指定方法(用委托WaitCallBack表示),并将该线程排入线程池的队列等待执行。

      3.doEvent.Set();
      将事件状态设置为终止状态,允许一个或多个等待线程继续。

      4.WaitHandle.WaitAll(doEvents);
       等待指定数组中的所有元素都收到信号。

    来看下面的例子,
    SendDealSucessEmailFromQueue()方法:threadCount是一个配置项,指定一次要开多少个线程,曾有人建议为了安全起见,机器是有好个核就开多少个线程这里假设是5,不知道有没有道理有待研究。dataNum也是一个配置项,指定一个线程要处理多少订单数据,这里假设是50。ManualResetEvent[] doEvents = new ManualResetEvent[threadCount],指定每次启动5个线程。然后循环doEvents[i] = new ManualResetEvent(false);使用false作为参数通知当前线程等待此线程完成之后再完成当前线程的工作。ThreadPool.QueueUserWorkItem(new WaitCallback(DealData), new object[] { i, doEvents[i] }); 在线程池中添加线程来执行DealData方法,给这个方法传递一个参数,这个参数是一个对象数组,数组的第一项是当前第几个线程,就是线程号,第二个是线程的事件(请原谅我这么翻译,貌似没有看到ManualResetEvent这个类的中文名字是什么,看样子像手动重置事件,我这里简称线程事件)。注意for循环外面的一句话WaitHandle.WaitAll(doEvents);,这里先不解释待说完循环中调用的DealData方法之后再说。

    DealData(object eventParams)方法:上面说到给他传递的参数是一个对象数组,所以int threadId = (int)(((object[])eventParams)[0]); ManualResetEvent doEvent = (ManualResetEvent)(((object[])eventParams)[1]);这两句很好理解,就是从这个对象数组中把实参值转换过来。int tmpCount = orderList.Count / threadCount;计算每个线程要处理的订单数量,要说的是因为要判断存在有要处理的订单才会开线程来处理,所以使用静态变量orderList。下面的这个for循环需要耐心琢磨,其实在SendDealSucessEmailFromQueue()这个方法执行完之后就已经开了5个线程,这时每个线程在做些什么事情我们是不能控制的,只知道有5个线程像脱缰野马一样地跑,在执行DealData(object eventParams)的时候是不知道当前是的线程是这5匹马中的那一匹,所以在这个for循环中我们就需要给他们分配工作,具体来说就是第0个线程处理0-49个订单,第1个线程处理50-99个,第2个线程处理100-149个,第3个线程处理200-249个,注意我这里都是用数组下标来解释。分配完即执行,执行完之后调用doEvent.Set();将当前线程置为终止。

    再回到SendDealSucessEmailFromQueue()方法的最后一句WaitHandle.WaitAll(doEvents);等待线程事件中的所有任务执行完成之后继续当前线程,就是继续往下跑了,就是主程序了,可以理解为回到Main()方法中了。

        /// <summary>
        /// 处理订单邮件
        /// </summary>
        public class OrderEmailProcess
        {
            /// <summary>
            /// 日志
            /// </summary>
            private static readonly ILog logger = LogManager.GetLogger(typeof(OrderEmailProcess));
            static List<Corp.CorpFlightADTKJob.Entity.FltOrderInfoEntity.OrderInfoEntity> orderList = new List<Entity.FltOrderInfoEntity.OrderInfoEntity>();
            static int threadCount = 0;
            static int dataNum = 0;
    
            /// <summary>
            /// 发送成交确认邮件
            /// 因为发邮件接口中占用了emailtype:8,所以使用4096,这里需要处理emailtype
            /// </summary>
            public static void SendDealSucessEmailFromQueue()
            {
                threadCount = int.Parse(ConfigurationManager.AppSettings["ThreadCount"]);
                dataNum = int.Parse(ConfigurationManager.AppSettings["DataNum"]);
                string logMsg = "";
                orderList = CorpProcessDB.GetDealConfirmEmailQueue(ConnStrFactory.CorpProcessDB_Select, threadCount * dataNum);
                if (orderList != null && orderList.Count > 0)
                {
                    //记录日志
                    for (int i = 0; i < orderList.Count; i++)
                    {
                        logMsg += string.Format("OrderID:{0} Uid:{1} EmailType:{2}", orderList[i].OrderID, orderList[i].Uid, orderList[i].EmailType) + "
    ";
                    }
                    logger.Info("自动发邮件", logMsg);
                    ManualResetEvent[] doEvents = new ManualResetEvent[threadCount];
                    for (int i = 0; i < threadCount; i++)
                    {
                        doEvents[i] = new ManualResetEvent(false);
                        ThreadPool.QueueUserWorkItem(new WaitCallback(DealData), new object[] { i, doEvents[i] });
                    }
                    WaitHandle.WaitAll(doEvents);
                }
            }
    
            /// <summary>
            /// 处理线程方法
            /// </summary>
            /// <param name="eventParams"></param>
            private static void DealData(object eventParams)
            {
                int threadId = (int)(((object[])eventParams)[0]);      //当前线程ID
                ManualResetEvent doEvent = (ManualResetEvent)(((object[])eventParams)[1]);
                int tmpCount = orderList.Count / threadCount;       //平均每个线程处理数据
    
                for (int i = 0; i < orderList.Count; i++)
                {
                    if (i < (threadId) * tmpCount)
                        continue;
    
                    if ((i >= (threadId + 1) * tmpCount) && threadId != (threadCount - 1))
                        break;
    
                    doDetailEvent(orderList[i]);
                }
                doEvent.Set();
            }
    
            /// <summary>
            /// 发邮件
            /// </summary>
            /// <param name="item"></param>
            /// <param name="threadId"></param>
            private static void doDetailEvent(Corp.CorpFlightADTKJob.Entity.FltOrderInfoEntity.OrderInfoEntity order)
            {
                int emailsendtime = order.EmailType;
                if ((emailsendtime & 8) == 8)
                {
                    emailsendtime = emailsendtime + 4088;
                }
                CorpEmaiWSlHelper.SendCorpConfirmMail(order.OrderID, order.Uid, "N", emailsendtime, ""); 
            }
        }

    如果你觉得上面的例子跑不起来,那么下面的例子可以让你在自己的机器上跑起来。这是一个web页面,OrderInfoEntity这个类写在另外一个文件中,这里和在一起展示。

        public partial class About : System.Web.UI.Page
        {
            int threadCount = 5;
            int dataNum = 50;
            static string msg = "";
            protected void Page_Load(object sender, EventArgs e)
            {
                msg = "";
                SendDealSucessEmailFromQueue();
                Response.Write(msg);
            }
    
            public List<OrderInfoEntity> orderList;
            public void SendDealSucessEmailFromQueue()
            {
                threadCount = 5;
                dataNum = 50;
                orderList = GetDealConfirmEmailQueue(threadCount * dataNum);
                if (orderList != null && orderList.Count > 0)
                {
                    ManualResetEvent[] doEvents = new ManualResetEvent[threadCount];
                    for (int i = 0; i < threadCount; i++)
                    {
                        doEvents[i] = new ManualResetEvent(false);
                        ThreadPool.QueueUserWorkItem(new WaitCallback(DealData), new object[] { i, doEvents[i] });
                    }
                    WaitHandle.WaitAll(doEvents);
                }
            }
    
            public void DealData(object eventParams)
            {
                int threadId = (int)(((object[])eventParams)[0]);      //当前线程ID
                ManualResetEvent doEvent = (ManualResetEvent)(((object[])eventParams)[1]);
                int tmpCount = orderList.Count / threadCount;       //平均每个线程处理数据
    
                for (int i = 0; i < orderList.Count; i++)
                {
                    if (i < (threadId) * tmpCount)
                        continue;
    
                    if ((i >= (threadId + 1) * tmpCount) && threadId != (threadCount - 1))
                        break;
    
                    doDetailEvent(orderList[i]);
                }
                doEvent.Set();
            }
            
            static void doDetailEvent(OrderInfoEntity order)
            {
                msg += order.OrderID.ToString() + "</br>";
            }
    
            /// <summary>
            /// 添加订单列表
            /// </summary>
            /// <param name="p"></param>
            /// <returns></returns>
            private static List<OrderInfoEntity> GetDealConfirmEmailQueue(int p)
            {
                List<OrderInfoEntity> result = new List<OrderInfoEntity>();
                for (int i = 0; i < p; i++)
                {
                    OrderInfoEntity entity = new OrderInfoEntity(i + 1);
                    result.Add(entity);
                }
                return result;
            }
        }
    
        public class OrderInfoEntity
        {
            private int _orderID;
            public int OrderID
            { get { return _orderID; } set { _orderID = value; } }
            public OrderInfoEntity(int orderid)
            {
                this._orderID = orderid;
            }
        }

     这个方法也是异步读取的方法,不过简单一些。

                    #region 异步读取数据
                    long logID1 = RequestManager.newSubRequest(), logID2 = RequestManager.newSubRequest(), logID3 = RequestManager.newSubRequest();
                    Task<List<CorpFlightSearchFlightsEntity>> taskNormalFlts = Task.Factory.StartNew<List<CorpFlightSearchFlightsEntity>>(() => SearchNormalFlights(searchRequest, logID1));
                    Task<List<CorpFlightSearchFlightsEntity>> taskCorpFlts = Task.Factory.StartNew<List<CorpFlightSearchFlightsEntity>>(() => SearchCorpFlightsOnly(searchRequest, logID2));
                    Task<List<CorpFlightSearchFlightsEntity>> taskMultiSpecialFlts = null;
                    if (searchRequest.FlightWay == "D")
                    {
                        taskMultiSpecialFlts = Task.Factory.StartNew<List<CorpFlightSearchFlightsEntity>>(() => SearchMultiSpecialFlights(searchRequest, logID3));
                        Task.WaitAll(taskNormalFlts, taskCorpFlts, taskMultiSpecialFlts);
                    }
                    else
                    {
                        Task.WaitAll(taskNormalFlts, taskCorpFlts);
                    }
    
                    normalFlts = taskNormalFlts.Result;
                    corpFlts = taskCorpFlts.Result;
                    multiSpecialFlts = taskMultiSpecialFlts != null ? taskMultiSpecialFlts.Result : null;
                    #endregion

     据新来的高手说下面这样写才能保证速度,上面的方法只能提高吞吐量,并不能防止阻塞。

                    Task<List<CorpFlightSearchFlightsEntity>> taskNormalFlts = Task.Factory.StartNew<List<CorpFlightSearchFlightsEntity>>(() => SearchNormalFlights(searchRequest, logID1));
                    Task<List<CorpFlightSearchFlightsEntity>> taskCorpFlts = Task.Factory.StartNew<List<CorpFlightSearchFlightsEntity>>(() => SearchCorpFlightsOnly(searchRequest, logID2));
                    Task<List<CorpFlightSearchFlightsEntity>> taskMultiSpecialFlts = null;
                    if (searchRequest.FlightWay == "D")
                    {
                        taskMultiSpecialFlts = Task.Factory.StartNew<List<CorpFlightSearchFlightsEntity>>(() => SearchMultiSpecialFlights(searchRequest, logID3));
                        //Task.WaitAll(taskNormalFlts, taskCorpFlts, taskMultiSpecialFlts);
                        normalFlts = taskNormalFlts.ContinueWith(t => t.Result).Result;
                        corpFlts = taskCorpFlts.ContinueWith(t => t.Result).Result;
                        multiSpecialFlts = taskMultiSpecialFlts.ContinueWith(t => t.Result).Result;
                    }
                    else
                    {
                        //Task.WaitAll(taskNormalFlts, taskCorpFlts);
                        normalFlts = taskNormalFlts.ContinueWith(t => t.Result).Result;
                        corpFlts = taskCorpFlts.ContinueWith(t => t.Result).Result;
                    }
    
                    normalFlts = taskNormalFlts.Result;
                    corpFlts = taskCorpFlts.Result;
                    multiSpecialFlts = taskMultiSpecialFlts != null ? taskMultiSpecialFlts.Result : null;

     还有一种简写方法,在没有前后依赖关系,没有返回值的情况下还可以使用Parallel.Invoke方法,如下:

                    if (this._appFlightsList.Count > 0)
                    {
                        Parallel.Invoke(() =>
                        {
                            
                            Parallel.ForEach(_appFlightsList, ShowFlightsUICommon.SetAppFlight);
                            
                            this._appFlightsList.Sort(AppFlightSorter.CorpSort);
                        },
                        () =>
                        {
                            
                            if (this._passengerTypeString.Trim().ToUpper() == "ADU")
                            {
                                
                                if (this._lowestPrices != null && this._lowestPrices.Count > 0)
                                {
                                    
                                    this._lowestPrices.Sort(LowestPriceSoter.CompareLowestPriceByDate);
                                }
                                
                                ShowFlightsUICommon.SetCorpSingleTripAppSpecialSummary4OnlineCNOnly(
                                    this._specialSummary, corpFlightsResponse.SingleTripSummaryList);
                                
                                if (this._corpSearchMultipleSpecialResponse != null &&
                                    this._corpSearchMultipleSpecialResponse.Groups.Count > 0)
                                {
                                    ShowFlightsUICommon.SetCorpMultipleAppSpecialSummary(
                                        this._specialSummary,
                                        this._corpSearchMultipleSpecialResponse.Groups);
                                }
                                
                                if (this._corpSearchTransitSpecialResponse != null &&
                                    this._corpSearchTransitSpecialResponse.LowGroups.Count > 0)
                                {
                                    ShowFlightsUICommon.SetCorpTransitAppSpecialSummary(
                                        this._specialSummary,
                                        this._corpSearchTransitSpecialResponse.LowGroups);
                                }
    
                                
                                ShowFlightsUICommon.SetCorpAppSpecialPrice(this._specialSummary,
                                                                            this._IsCalcNetPrice,
                                                                            this._SaleRate);
                                
                                FlightProcess.FlightsFilterWithAccount(this._specialSummary,
                                                                        this._AccountID);
                            }
                        });
                    }
  • 相关阅读:
    基于jackson的kotlin json字符串对比器
    python之一mac安装HTMLTestRunner.py
    python之插件安装
    饿了么三面:让你怀疑人生的Spring Boot夺命连环40问
    网易三面:说说你对MySQL事务四大隔离的理解
    京东Java岗:来自面试官的夺命连环56问,你怀疑人生了吗?
    QPushButton
    宏相关使用
    C++类构造析构基础
    P8261 [CTS2022] 袜子 解题报告
  • 原文地址:https://www.cnblogs.com/tylerdonet/p/3160338.html
Copyright © 2020-2023  润新知