最近异步和多线程一直困扰着我,我也将用一定的时间去慢慢的理解。突然想到petshop里面有用到异步和多线程,自己也看了,还是总结下好。
首先,要讲petshop的异步还是要从order订单策略开始讲起,先看下IBLLStrategy.IOrderStrategy这个接口:
public interface IOrderStrategy { void Insert(PetShop.Model.OrderInfo order); }
然后看下BLL对订单的两种实现方式,一种是异步一种是同步的,同步的就不必多说了,就直接看下异步的吧:
public class OrderAsynchronous : IOrderStrategy { // Get an instance of the MessagingFactory // Making this static will cache the Messaging instance after the initial load private static readonly PetShop.IMessaging.IOrder asynchOrder = PetShop.MessagingFactory.QueueAccess.CreateOrder(); /// <summary> /// This method serializes the order object and send it to the queue for asynchronous processing /// </summary> /// <param name="order">All information about the order</param> public void Insert(PetShop.Model.OrderInfo order) { asynchOrder.Send(order); } }
这个OrderAsynchronous的实现是通过MessagingFactory工厂类来发送order消息的,具体怎么实现这边就不细说了,由于本人的博客一般地写给自己看,所以就没有写得那么详细了。
这个订单的异步是分两步进行的:第一,吧订单插入到消息队列中,第二,做一个后台的控制程序,实时地吧消息队列的值插入到数据库中。这样的话更好的提高网站的性能。
那么,这个时候是不是要来看下这个后台的控制程序是怎么运行的呢?
static void Main() { Thread workTicketThread; Thread[] workerThreads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { workTicketThread = new Thread(new ThreadStart(ProcessOrders)); // Make this a background thread, so it will terminate when the main thread/process is de-activated workTicketThread.IsBackground = true; workTicketThread.SetApartmentState(ApartmentState.STA); // Start the Work workTicketThread.Start(); workerThreads[i] = workTicketThread; } Console.WriteLine("Processing started. Press Enter to stop."); Console.ReadLine(); Console.WriteLine("Aborting Threads. Press wait..."); //abort all threads for (int i = 0; i < workerThreads.Length; i++) { workerThreads[i].Abort(); } Console.WriteLine(); Console.WriteLine(totalOrdersProcessed + " Orders processed."); Console.WriteLine("Processing stopped. Press Enter to exit."); Console.ReadLine(); } /// <summary> /// Process a batch of asynchronous orders from the queue and submit them to the database within a transaction /// </summary> private static void ProcessOrders() { // the transaction timeout should be long enough to handle all of orders in the batch TimeSpan tsTimeout = TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout * batchSize)); Order order = new Order(); while (true) { // queue timeout variables TimeSpan datetimeStarting = new TimeSpan(DateTime.Now.Ticks); double elapsedTime = 0; int processedItems = 0; ArrayList queueOrders = new ArrayList(); //OrderInfo orderData = orderQueue.Receive(timeout); using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, tsTimeout)) { // Receive the orders from the queue for (int j = 0; j < batchSize; j++) { try { //only receive more queued orders if there is enough time if ((elapsedTime + queueTimeout + transactionTimeout) < tsTimeout.TotalSeconds) { queueOrders.Add(order.ReceiveFromQueue(queueTimeout)); } else { j = batchSize; // exit loop } //update elapsed time elapsedTime = new TimeSpan(DateTime.Now.Ticks).TotalSeconds - datetimeStarting.TotalSeconds; } catch (TimeoutException) { //exit loop because no more messages are waiting j = batchSize; } } //process the queued orders for (int k = 0; k < queueOrders.Count; k++) { order.Insert((OrderInfo)queueOrders[k]); processedItems++; totalOrdersProcessed++; } //batch complete or MSMQ receive timed out ts.Complete(); } Console.WriteLine("(Thread Id " + Thread.CurrentThread.ManagedThreadId + ") batch finished, " + processedItems + " items, in " + elapsedTime.ToString() + " seconds."); } }
首先,定义了线程数组,这里的threadcount是2,那么 就是两个线程同时在运行,提高执行效率。然后看下线程执行的函数ProcessOrders吧,他可以分为两步,第一是吧消息队列的值ArrayList,然后再从中取值,最后插入到数据库中。
这边的异步是人为的都,二并非某某的Begin** End**.
---212-7-25
之前对为什么这边会用多线程没有多加思考,只觉得用了多线程就是好,但是多线程该怎么样,何时用?下面贴出我今晚测试的代码:
public static void DoSomeWork() { ///构造显示字符串 string results = ""; ///创建一个Sigleton实例 CountSigleton MyCounter = new CountSigleton().Instance(); ///循环调用四次 for(int i=1;i<5;i++) { ///开始计数 MyCounter.Add(); results +="线程"; results += Thread.CurrentThread.Name + "——〉"; results += "当前的计数:"; results += MyCounter.GetCounter().ToString(); results += "\n"; Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread+"\n"); Console.WriteLine(results); ///清空显示字符串 results = ""; } }
为了不产生误解,我把CountSigleton类也贴出来,之前这个类是一个单例模式的,我改了。
public class CountSigleton { ///存储唯一的实例 private CountSigleton uniCounter; ///存储计数值 private int totNum = 0; public CountSigleton() { ///线程延迟2000毫秒 Thread.Sleep(2000); } public CountSigleton Instance() { uniCounter = new CountSigleton(); return uniCounter; } ///计数加1 public void Add() { totNum ++; } ///获得当前计数值 public int GetCounter() { return totNum; }
然后来看看多线程调用吧:
Thread threadTest; Thread[] workerThreads = new Thread[2]; for (int i = 0; i < 2; i++) { threadTest = new Thread(new ThreadStart(DoSomeWork)); threadTest.Name = "thread" + i; threadTest.SetApartmentState(ApartmentState.STA); threadTest.IsBackground = true; workerThreads[i] = threadTest; threadTest.Start(); }
这边是两个线程,直接看控制台输出的结果吧,看结果比较好说明什么:
这就有点像线程的两次运行,这样的结果显然不是我们想得到的。
那么这样的多线程到底有什么作用呢?好的,原归正状,petshop这边消息队列为什么要用多线程呢?
因为,这样可以跟快的事实的去取到消息队列的值。这边就是多线程的魅力所在。