petshop异步和多线程
最近异步和多线程一直困扰着我,我也将用一定的时间去慢慢的理解。突然想到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这边消息队列为什么要用多线程呢?
因为,这样可以跟快的事实的去取到消息队列的值。这边就是多线程的魅力所在。

浙公网安备 33010602011771号