多线程并发删除日志小工具
2016年过年后没什么新任务,于是自己做了个定时删除日志的小工具,方便以后的工作中用到,也希望能给大家一点帮助,有不对的地方多多指教。
功能不是很复杂,主要实现了以下几个功能:
1.定时检测本地磁盘空间,当空间不足时,做出相应的提醒。
2.支持动态创建N个磁盘定时删除任务。
3.支持多线程并发删除操作,避免出现锁死现象。
4.能够同时支持文件的删除和子文件夹的删除。
废话不多说,先上具体代码:
- 删除文件夹里的文件方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | private bool ToDeleteFile( string url) { ListViewItem listItem = null ; if ( string .IsNullOrWhiteSpace(url)) return false ; else { //遍历文件夹里的所有文件 try { fileCount = 0; DirectoryInfo info = new DirectoryInfo(url); foreach (FileInfo file in info.GetFiles()) { DateTime lastDate = file.LastWriteTime; //获取文件最后一次写入时间 DateTime dtNow = DateTime.Now; TimeSpan ts = dtNow - lastDate; if (sysTie.Enabled == true ) { if (ts.Days > int .Parse(DeleteLogDays)) { fileSize += ( float .Parse(file.Length.ToString()) / (1024 * 1024)); file.Delete(); fileCount++; } } } listItem = AddListViewData( "成功!--删除文件" , "删除" + url + "里的日志文件" + fileCount + "个" , DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" )); listItem.ForeColor = Color.Blue; if (fileCount > 0) { this .lstMessage.Items.Add(listItem); lbl.Text = fileSize.ToString( "F2" ) + " M" ; } return true ; } catch { return false ; } } } |
- 删除文件夹里的子文件夹方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | private bool ToDeleteFolder( string url) { ListViewItem listItem = null ; if ( string .IsNullOrWhiteSpace(url)) return false ; else { //遍历文件夹里的所有子文件夹 try { fileCount = 0; DirectoryInfo info = new DirectoryInfo(url); foreach (DirectoryInfo floder in info.GetDirectories()) { DateTime lastDate = floder.LastWriteTime; //获取文件最后一次写入时间 DateTime dtNow = DateTime.Now; TimeSpan ts = dtNow - lastDate; if (sysTie.Enabled == true ) { if (ts.Days > int .Parse(DeleteFolderDays)) { fileSize += ( float .Parse(GetFloderFileSize(floder.GetFiles()).ToString()) / (1024 * 1024)); floder.Delete( true ); fileCount++; } } } listItem = AddListViewData( "成功!--删除子文件夹" , "删除" + url + "里的子文件夹" + fileCount + "个" , DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" )); listItem.ForeColor = Color.Blue; if (fileCount > 0) { this .lstMessage.Items.Add(listItem); lbl.Text = fileSize.ToString( "F2" ) + " M" ; } return true ; } catch { return false ; } } } |
下面是定时删除任务:
- 定时删除文件夹里的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private void StartUpTimerFile( string url) { ListViewItem listItem = null ; if (!IsUrlExist(url)) { listItem = AddListViewData( "失败!--启动删除文件" , "启动" + url + "路径失败,没有找到对应的文件夹" , DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" )); listItem.ForeColor = Color.Red; } else listItem = AddListViewData( "成功!--启动删除文件" , "启动删除文件任务成功,已开始运行,路径:" + url, DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" )); this .lstMessage.Items.Add(listItem); sysTie = new System.Timers.Timer( int .Parse(logTime)); sysTie.Elapsed += (o, s) => { if (DateTime.Now.Hour == int .Parse(deleteLogTime)) { DelegateToDeleteFile del = new DelegateToDeleteFile(ToDeleteFile); BeginInvoke(del, url); } }; sysTie.Start(); } |
- 定时删除文件夹里的子文件夹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private void StartUpTimerFolder( string url) { ListViewItem listItem = null ; if (!IsUrlExist(url)) { listItem = AddListViewData( "失败!--启动删除子文件夹" , "启动" + url + "路径失败,没有找到对应的文件夹" , DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" )); listItem.ForeColor = Color.Red; } else listItem = AddListViewData( "成功!--启动删除子文件夹" , "启动删除子文件夹任务成功,已开始运行,路径:" + url, DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" )); this .lstMessage.Items.Add(listItem); sysTie = new System.Timers.Timer( int .Parse(FolderTime)); sysTie.Elapsed += (o, s) => { if (DateTime.Now.Hour == int .Parse(DeleteFolderTime)) { DelegateToDeleteFile del = new DelegateToDeleteFile(ToDeleteFolder); BeginInvoke(del, url); } }; sysTie.Start(); } |
由于调用方法时,会报异常,所以这块我采用委托来加载上面的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /// <summary> /// 委托开始方法--删除文件 /// </summary> /// <param name="url"></param> public void DelegateMethodFile( string url) { MyDelegateTimer delTimer = new MyDelegateTimer(StartUpTimerFile); BeginInvoke(delTimer, url); } /// <summary> /// 委托开始方法--删除子文件夹 /// </summary> /// <param name="url"></param> public void DelegateMethodFolder( string url) { MyDelegateTimer delTimer = new MyDelegateTimer(StartUpTimerFolder); BeginInvoke(delTimer, url); } |
以上代码单个删除任务的执行,如何实现多个删除任务的并发运行,因为在我写的时候出现了如果我启动多个删除任务的时候stop()上一个运行的任务,只会运行刚刚启动的任务,所以这块我使用了一个高效的线程安全队列ConcurrentQueue,坚持先进先出的原则。
- 创建一个可以由多个线程同时访问的键值集合ConcurrentDictionary<string, string>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public ConcurrentDictionary< string , string > WriteQueue( string s, string prefix) { if ( string .IsNullOrWhiteSpace(s)) return null ; string val = s; string key = prefix; _logQueue.Enqueue( new KeyValuePair< string , string >(key, val)); var dic = GetLogText(); return dic; } /// <summary> /// 写入队列操作 /// </summary> /// <returns></returns> private ConcurrentDictionary< string , string > GetLogText() { do { KeyValuePair< string , string > kv; if (_logQueue.TryDequeue( out kv)) { dict.AddOrUpdate(kv.Key, kv.Value, (k, v) => string .Concat(v + "\r\n" + kv.Value)); } } while (_logQueue.Count > 0); return dict; } |
下面是启动多线程事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | private void button1_Click( object sender, EventArgs e) { this .lstMessage.Items.Clear(); dict = new ConcurrentDictionary< string , string >(); _logQueue = new ConcurrentQueue<KeyValuePair< string , string >>(); if (strBackList == null || strBackList.Count == 0) { MessageBox.Show( "路径不能为空,请输入详细地址!" ); return ; } foreach (KeyValuePair< string , string > obj in strBackList) { if (! string .IsNullOrWhiteSpace(obj.Value)) { string strKey = obj.Key; string [] s = obj.Value.Split( '|' ); if (s.Length > 0) { foreach ( var v in s) { if (! string .IsNullOrWhiteSpace(v)) { dict = WriteQueue(v, string .Format( "{0}-{1}" , strKey.Substring(0, 1), clickCount.ToString())); clickCount++; } } } } } if (dict != null ) { lock (_lockObject) { foreach ( var kv in dict) { if (! string .IsNullOrWhiteSpace(kv.Key)) { switch ( int .Parse(kv.Key.Substring(0, 1))) { //All case 0: DelegateMethodFile(kv.Value); DelegateMethodFolder(kv.Value); break ; case 1: //file DelegateMethodFile(kv.Value); break ; case 2: //folder DelegateMethodFolder(kv.Value); break ; default : //none break ; } } } } } this .button1.Enabled = false ; button1.Text = "任务执行中..." ; } |
这样就实现了多线程之间的并发执行,下面是页面效果图
上面就是我自己这次的总结,只上传了些核心代码,有不对的地方尽请谅解,只是个人的学习记录。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法