多线程数据写入队列,异步线程进行批量处理

设计目的:

在多线程环境中,多线程处理数据时,如果每线程都单独写数据库,性能低下。因此,为提高性能,数据需批量写到数据库中。出于此目的,进行了数据队列的设计:

 

实现代码:

 

  1 /// <summary>
  2     /// 多线程异步推送数据,一线程异步批量处理数据
  3     /// </summary>
  4     /// <typeparam name="T"></typeparam>
  5     public abstract class SingleThreadQueue<T>
  6     {
  7         protected List<T> queue;
  8         private Action _beforAction;//在批量执行前执行的方法
  9         private Action _afterAction;//在批量执行完成后执行的方法  
 10         private bool _runing = false;
 11         private int _maxCount, _maxSize;
 12 
 13         /// <summary>
 14         /// 
 15         /// </summary> 
 16         /// <param name="maxCount">每次最大处理数</param>
 17         /// <param name="maxSize">缓冲最大数</param>
 18         /// <param name="beforAction">开始执行处理前的动作</param>
 19         /// <param name="afterAction">所有数据处理完成后的动作</param>
 20         public SingleThreadQueue(int maxCount, int maxSize, Action beforAction = null, Action afterAction = null)
 21         {
 22             this.queue = new List<T>();
 23             this._beforAction = beforAction;
 24             this._afterAction = afterAction;
 25             this._maxCount = maxCount;
 26             this._maxSize = maxSize;
 27         }
 28 
 29         /// <summary>
 30         /// 异步推送数据
 31         /// </summary>
 32         /// <param name="item"></param>
 33         /// <returns>数据成功加入待处理队列,返回true,数据达到队列的最大缓冲,数据不会进入队列,返回false</returns>
 34         public bool AsyncEnqueue(T item)
 35         {
 36             lock (this)
 37             {
 38                 if (this.queue.Count >= this._maxSize)
 39                     return false;
 40 
 41                 this.queue.Add(item);
 42                 this.Activate();
 43                 return true;
 44             }
 45         }
 46 
 47         protected T[] DoDequeue(List<T> queue)
 48         {
 49             if (queue.Count > this._maxCount)
 50             {
 51                 var ds = queue.Take(this._maxCount).ToArray();
 52                 queue.RemoveRange(0, this._maxCount);
 53                 return ds;
 54             }
 55             else
 56             {
 57                 var ds = queue.ToArray();
 58                 queue.Clear();
 59                 return ds;
 60             }
 61         }
 62 
 63         /// <summary>
 64         /// 实现此方法,批量执行处理时的方法
 65         /// </summary>
 66         /// <param name="items"></param>
 67         protected abstract void OnExecute(T[] items);
 68 
 69         private void Activate()
 70         {
 71             if (this._runing)
 72             {
 73                 return;
 74             }
 75 
 76             this._runing = true;
 77             ThreadPool.QueueUserWorkItem((obj) =>
 78             {
 79                 try
 80                 {
 81                     this._beforAction?.Invoke();
 82 
 83                     T[] items;
 84                     //管理线程
 85                     while (true)
 86                     {
 87                         lock (this)
 88                         {
 89                             if (queue.Count < 1)
 90                             {
 91                                 this._afterAction?.Invoke();
 92                                 this._runing = false;
 93                                 break;
 94                             }
 95 
 96                             items = this.DoDequeue(queue);
 97                         }
 98 
 99                         try
100                         {
101                             this.OnExecute(items);
102                         }
103                         catch (Exception ex)
104                         {
105                             //TODO:异常预警
106                             // Loger.Exception(ex.Message, ex);
107                         }
108                     }
109                 }
110                 catch (Exception ex)
111                 {
112                     //TODO:异常预警
113                     // Loger.Exception(ex.Message, ex);
114                 }
115             });
116         }
117 
118     }

 经测试,多线程单独写入DB,在开发环境,100/秒,但批量处理可达5000/秒。

转载于:https://www.cnblogs.com/likg/p/10458776.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
上传项目不支持Firefox,提示代码附件太大(1.4M),我写了30多分钟的描述全没了,太坑爹了。 10分有点贵,绝对原创,共2个代码文件300多行,下载请谨慎。你下载了,若绝对不爽在评论说出来,不要让其他同学上当,如果觉得还可以也请留言。 代码采用多工作者多线程执行任务。通过暴露的方法往工作者传递消息,然后采用事件回调返回处理结果,实现的事件有OnThreadComplete,OnAddedTask,OnStart,OnSuccess,OnFailure,OnTimeout。 事件回调支持同步或异步,每工作者可以指定执行超时时间,避免线程阻塞死掉。队列采用线程安全的BlockingCollection,每组工作者用一个队列。委托采用Func来定义的,没有采用传统且不太好理解的Delegate。这让代码减少很多,也更容易理解。多线程应该采用消息心来交换数据,这样就规避了线程同步交互,等待,阻塞等等,全部是异步调用,全部是接收消息工作,然后产生消息,线程间没有耦合,消息心有很多成熟的方案如RabbitMQ, Redis(里面有简单的消息交换),微软有消息云服务等。如果应用不复杂,可以采用DB做个简单的消息心,建议采用HTTP接口来获取与写入消息,方便将来升级重构消息心。 开发环境VS2012,Framework4.0,代码注释量很大,如果你高兴这代码你可以随意蹂躏,如果你有建设性意见请告诉我。 下面是部分测试代码: //发送消息方法容器 var msgContainer = new Hashtable(); //创建并启动工作者 foreach (var key in workers.Keys) { //创建工作者 //启动5个线程,异步事件回调,方法执行20秒超时,程序跑起来有100个线程,由于引入超时控制,实际线程将达100+50 //下面的20个工作组,有5个是超时的,主要测试OnTimeout事件,你可以设置seleep的时间来控制 //我把sleep的时间设置的有点长,方便你测试 //测试的时候你会看见有异常,那是应为Timeout我采用的是Thread.Abort方法,这样才出发了ontimeout事件 var worker = new Sehui.Worker(5, key.ToString(), (Func)workers[key], false, new TimeSpan(0, 0, 20)); worker.OnStart += worker_OnEvent; worker.OnSuccess += worker_OnEvent; worker.OnFailure += worker_OnEvent; worker.OnTimeout += worker_OnEvent; //启动工作者 worker.Start(); //将增加消息方法放到Hashtable //这里我是偷懒,下面可以用循环的方式往线程add message msgContainer.Add(key.ToString(), new Func(worker.AddTask)); } //向20个工作者发送消息,每个工作者发送20条消息 for (var i = 0; i < 20; i++) { for (var k = 0; k < 20; k++) { ((Func)msgContainer["SyncDb" + k])("[Work " + k + "] Message " + i); Console.WriteLine("send msg to worker{0},msgid:{1}", k, i); } }
Spring Boot可以通过使用多线程来解决顺序写入Workbook的问题。 在Spring Boot,可以使用内置的ThreadPoolTaskExecutor来实现多线程的管理和调度。首先,我们可以在应用程序的配置文件配置ThreadPoolTaskExecutor的参数,如线程池的大小、队列容量等。 接下来,我们需要使用Spring Boot的异步任务注解@Async来标记我们的导出方法。这样,当调用导出方法时,Spring Boot就会将该方法放入线程执行,而不会阻塞主线程。 在导出方法,我们可以使用Apache POI等工具来创建Workbook,并在多个线程向Workbook写入数据。每个线程负责处理一定数量的数据。为了保证数据的顺序写入,我们可以使用线程安全的数据结构,如ConcurrentLinkedQueue或BlockingQueue来保存每个线程处理的数据。在每个线程处理完数据后,将数据按照顺序写入Workbook。 当所有线程都处理完数据后,我们可以将Workbook保存到文件,或者直接返回给前端。 需要注意的是,多线程写入Workbook可能存在线程安全的问题,如多个线程同时写入同一个单元格。为了解决这个问题,我们可以使用同步锁或原子操作来保证数据的一致性。此外,我们还可以使用线程池的返回结果Future来获取每个线程的执行结果并进行处理。 综上所述,Spring Boot可以通过使用多线程线程池来解决多线程导出顺序写入Workbook的问题。通过合理的设计和使用线程安全的数据结构,我们可以实现高效、稳定的导出功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值