C# multi-threaded directory copy file copy

Recently encountered a problem in the upgrade of an old project. Record it for later review. I am a novice, and I still need expert guidance to answer Xiao Z's questions.

Doubt: In Window7, the test direct copy file is about 250 items/second. However, by controlling the number of threads in the program and copying by assigning the tasks of the threads, the speed has not always reached the ideal value.

Problem: The image attachments of the old project are stored in the DownFile and UpFile folders respectively (divided into two types of files). After the system runs for a long time, there are more and more files in it, which is very troublesome to manage. Every time I open these two folders it is always unresponsive.

New project requirements: These two types of files are stored in folders on a monthly basis, the file names remain unchanged, and the file root path format is /DownFile/yyMM. Copy out image attachments by writing a small program.

The naming rules for files stored in the old directory DownFile are: (DateTime.Now.Ticks) + extension  .

The naming convention for files in the directory UpFile is: (uppercase "R" + yyMMddhhmmssfff) + extension .

The storage path is determined according to the year and month extracted from the file name. The way to copy the file will now be shown in the code.

Program configuration file: app.config

 
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <!--Number of threads-->
    <add key="ThreadCount" value="8" />
    <!--File source path DownFile;UpFile-->
    <add key="SourceDir" value="C:\Users\Administrator\Desktop\DownFile;C:\Users\Administrator\Desktop\UpFile" />
    <!--Target storage path-->
    <add key="ToDir" value="C:\Users\Administrator\Documents\Visual Studio 2008\Projects\ThreadCopyFile\CopyFileToDirectory\bin\Debug\CopyFolder" />
  </appSettings>
</configuration>
 

Code file: ThreadCopyFile2.cs

 
/// <summary>
/// Multithreading - parameter class
/// </summary>
public class SplitArr
{
    public int thread_id { get; set; }
    public int start_index { get; set; }
    public int end_index { get; set; }
}
 

 
/// <summary>
/// Multi-threaded file copy
/// </summary>
public class ThreadCopyFile2
{
    #region property
    private List<Thread> threadList = new List<Thread>();//线程列表
    private event EventHandler OnNumberClear;//Event triggered by the completion of task execution
    private Stopwatch sw = new Stopwatch();//Time count
    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

}
  

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325559131&siteId=291194637