最近在旧项目升级中遇到一个问题。把它记录下来,以便日后查看。本人新手,其中不足还需高手指点,解答小z的疑问。
疑问:在Window7中测试直接拷贝文件在250项/秒左右。但是在通过程序中控制线程数量,并且通过分配线程的任务来拷贝,始终速度都没有达到理想值。
问题:旧项目在存放图片附件是分别存放在DownFile 、UpFile 两个文件夹内(分为两类文件),系统运行时间一久,里面文件也越来越多,管理起来非常麻烦。每次在打开这两个文件夹总是无响应。
新项目需求:这两类文件分别是月为单位存放在文件夹内,文件名保持不变,文件根路径格式/DownFile/yyMM。通过写一个小程序来拷出图片附件。
旧目录DownFile中存放的文件命名规则是:(DateTime.Now.Ticks)+扩展名 。
目录UpFile中文件命名规则是:(大写"R" + yyMMddhhmmssfff) + 扩展名。
根据文件名字中提取年月来决定存放路径。现将拷贝文件的方式在代码中展示。
程序配置文件:app.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<!--线程数量-->
<add key="ThreadCount" value="8" />
<!--文件源路径 DownFile;UpFile-->
<add key="SourceDir" value="C:\Users\Administrator\Desktop\DownFile;C:\Users\Administrator\Desktop\UpFile" />
<!--目标存放路径-->
<add key="ToDir" value="C:\Users\Administrator\Documents\Visual Studio 2008\Projects\ThreadCopyFile\CopyFileToDirectory\bin\Debug\CopyFolder" />
</appSettings>
</configuration>
代码文件:ThreadCopyFile2.cs
/// <summary>
/// 多线程-参数类
/// </summary>
public class SplitArr
{
public int thread_id { get; set; }
public int start_index { get; set; }
public int end_index { get; set; }
}
/// <summary>
/// 多线程文件拷贝
/// </summary>
public class ThreadCopyFile2
{
#region 属性
private List<Thread> threadList = new List<Thread>();//线程列表
private event EventHandler OnNumberClear;//任务执行完成引发的事件
private Stopwatch sw = new Stopwatch();//时间计数
private int counter = 1;//处理任务的个数
private List<string> stringList;//文件列表
private int total = 0;//文件总个数
#endregion
/// <summary>
/// 程序入口
/// </summary>
public static void Main()
{
ThreadCopyFile2 tcf = new ThreadCopyFile2();
tcf.StartWrok();
}
#region 线程工作内容
/// <summary>
/// 初始化-并启动工作线程
/// </summary>
public ThreadCopyFile2()
{
//加载文件列表
InitFileList();
//初始进度条
ProcessBarInit();
int ThreadCount = Convert.ToInt32(ConfigurationManager.AppSettings["ThreadCount"]);
//设置线程工作范围
Dictionary<int, int[]> root = Splice(stringList, ThreadCount);
//设置线程
for (int i = 0; i < ThreadCount; i++)
{
Thread thread = new Thread(new ParameterizedThreadStart(CopyFile));
thread.Name = i.ToString();//设置线程ID
//线程工作范围
SplitArr sa = new SplitArr();
sa.thread_id = i;
sa.start_index = root[i][0];
sa.end_index = root[i][1];
thread.Start(sa);//启动线程
threadList.Add(thread);
}
//线程完成执行事件
OnNumberClear += new EventHandler(CompleteEvent);
}
/// <summary>
/// 加载文件列表
/// </summary>
private void InitFileList()
{
var SourceDir = ConfigurationManager.AppSettings["SourceDir"];
string[] path = SourceDir.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
//获取文件列表
stringList = new List<string>();
stringList.AddRange(Directory.GetFiles(path[0]).Where<string>(f => CheckExtion(f)));
stringList.AddRange(Directory.GetFiles(path[1]).Where<string>(f => CheckExtion(f)));
total = stringList.Count;//文件总个数
}
/// <summary>
/// 开始工作
/// </summary>
public void StartWrok()
{
//计时器-开始工作
sw.Start();
}
/// <summary>
/// 线程锁
/// </summary>
readonly object obj = new object();
/// <summary>
/// 文件拷贝
/// </summary>
private void CopyFile(object _SplitArr)
{
SplitArr sa = _SplitArr as SplitArr;
if (sa != null)
{
var ToDir = ConfigurationManager.AppSettings["ToDir"];
string newPath = String.Empty;//新路径
string newFilePath = String.Empty;//文件新全路径
FileInfo fi;
int rate;
//Console.WriteLine("ID:" + sa.thread_id + "\tsa.start_index:" + sa.start_index + "\tsa.end_index:" + sa.end_index);
int tempNum = sa.start_index;
while (tempNum <= sa.end_index)
{
string oldFilePath = stringList[tempNum];
fi = new FileInfo(oldFilePath);
//根据Source文件上一级路径做条件,返回相应新的存放路径
newPath = Path.Combine(ToDir, Path.Combine(fi.Directory.Name, AnalysisName(fi.Name, fi.Directory.Name)));
newFilePath = Path.Combine(newPath, fi.Name);
//判断新路径是否存在
if (!Directory.Exists(newPath)) { Directory.CreateDirectory(newPath); }
//判断目标路径是否存在该文件
if (!File.Exists(newFilePath) && File.Exists(fi.FullName))
{
File.Copy(fi.FullName, newFilePath);
删除ArrayList中的元素
//stringList.RemoveAt(0);
//stringList.TrimExcess();
//累加任务数
counter++;
//判断是否处理完任务
if (stringList.Count == counter)
{
OnNumberClear(this, new EventArgs());//引发完成事件
}
//计算百分比
rate = CalcRate(counter);
//标题设置
Console.Title = "正在拷贝第【" + counter.ToString() + "】个文件;进度" + rate.ToString() + "%";
//设置进度条
SetProcessBar(rate - 1);
//等待
Thread.Sleep(1);
}
tempNum++;
}
}
}
/// <summary>
/// 执行完成执行的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void CompleteEvent(object sender, EventArgs e)
{
sw.Stop();
Console.SetCursorPosition(0, 7);
Console.Write("执行完成,停止了所有线程的执行。成功拷贝文件:" + total.ToString() + "个,用时:" + sw.ElapsedMilliseconds.ToString() + "毫秒");
Console.Title = "成功拷贝文件【" + total.ToString() + "】个";
foreach (Thread thread in threadList)
{
thread.Abort();
}
}
#endregion
#region 公共函数
/// <summary>
/// 文件类型检查
/// </summary>
/// <param name="ext"></param>
/// <returns></returns>
bool CheckExtion(string ext)
{
return ext.EndsWith(".bmp") || ext.EndsWith(".jpg") || ext.EndsWith(".jpeg") || ext.EndsWith(".png") || ext.EndsWith(".gif") || ext.EndsWith(".tif");
}
/// <summary>
/// 根据文件名分析存放路径
/// </summary>
/// <param name="fileName"></param>
/// <param name="flag"></param>
/// <returns></returns>
string AnalysisName(string fileName, string flag)
{
switch (flag)//根据标识判断要返回的路径
{
case "DownFile":
string name1 = Regex.Match(fileName, "\\d*(?=\\.)").Value;
var time = new DateTime(Convert.ToInt64(name1));
return time.ToString("yyMM");
case "UpFile":
return Regex.Match(fileName, "(?<=R)\\d{4}").Groups[0].Value;
default:
return "";
}
}
/// <summary>
/// 分割集合
/// </summary>
/// <param name="oldStr">要分割的字符串</param>
/// <param name="length">分割后次数</param>
/// <returns></returns>
private static Dictionary<int, int[]> Splice(List<string> oldArray, int length)
{
Dictionary<int, int[]> root = new Dictionary<int, int[]>();
int count = oldArray.Count;
if (count > length)
{
int num = count / length;
int last = count % length;//剩余的元素个数
bool temp = false;
if (last > 0)
{
num++;
temp = true;
}
int tempNum = 0;
for (int i = 1; i <= length; i++)
{
if ((i - 1) == 0)
{
int rst = (temp ? num - 1 : num) - 1;
root.Add(i - 1, new int[] { 0, rst });
tempNum = rst + 1;
}
else if (i == length)
{
root.Add(i - 1, new int[] { tempNum, count - 1 });
}
else
{
int rst = root[0][1] * i + 1;
root.Add(i - 1, new int[] { tempNum, rst });
tempNum = rst;
}
}
}
else
{
root.Add(0, new int[] { length });
}
return root;
}
#endregion
#region 进度条
/// <summary>
/// 初始化进度条
/// </summary>
void ProcessBarInit()
{
Console.WriteLine("\n************************工作进度************************\n");
Console.Write(" ");
Console.BackgroundColor = ConsoleColor.Gray;
for (int i = 1; i <= 50; i++)
{
Console.Write(" ");
}
Console.BackgroundColor = ConsoleColor.Black;
Console.Write("");
Console.SetCursorPosition(0, 4);
Console.Write("\n********************************************************\n");
}
/// <summary>
/// 设置进度
/// </summary>
/// <param name="position"></param>
void SetProcessBar(int position)
{
Monitor.Enter(obj);
//绘制进度条进度
Console.BackgroundColor = ConsoleColor.Green;//设置进度条颜色
Console.SetCursorPosition(position / 2 + 1, 3);//设置光标位置,参数为第几列和第几行
Console.Write(" ");//移动进度条
Console.BackgroundColor = ConsoleColor.Black;//恢复输出颜色
Console.SetCursorPosition(52, 3);//设置光标位置,参数为第几列和第几行
Console.Write((position + 1).ToString() + "%");//移动进度条
Monitor.Exit(obj);
}
/// <summary>
/// 计算百分比
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
int CalcRate(int count)
{
int result = Convert.ToInt32((float)count / (float)total * 100);
return result;
}
#endregion
}