多线程并发删除日志小工具
2016年过年后没什么新任务,于是自己做了个定时删除日志的小工具,方便以后的工作中用到,也希望能给大家一点帮助,有不对的地方多多指教。
功能不是很复杂,主要实现了以下几个功能:
1.定时检测本地磁盘空间,当空间不足时,做出相应的提醒。
2.支持动态创建N个磁盘定时删除任务。
3.支持多线程并发删除操作,避免出现锁死现象。
4.能够同时支持文件的删除和子文件夹的删除。
废话不多说,先上具体代码:
- 删除文件夹里的文件方法
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; } } }
- 删除文件夹里的子文件夹方法
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; } } }
下面是定时删除任务:
- 定时删除文件夹里的文件
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(); }
- 定时删除文件夹里的子文件夹
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(); }
由于调用方法时,会报异常,所以这块我采用委托来加载上面的方法:
/// <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>
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; }
下面是启动多线程事件:
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 = "任务执行中..."; }
这样就实现了多线程之间的并发执行,下面是页面效果图
上面就是我自己这次的总结,只上传了些核心代码,有不对的地方尽请谅解,只是个人的学习记录。