C# 多线程目录拷贝 文件拷贝

最近在旧项目升级中遇到一个问题。把它记录下来,以便日后查看。本人新手,其中不足还需高手指点,解答小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

}
  

猜你喜欢

转载自blog.csdn.net/sayesan/article/details/80017117