首页 > 代码库 > 多线程处理大量文件

多线程处理大量文件

上周做了一个多线程处理大量文件的功能 一是记录 二是分享 三是请博友指出不足 更多的了解多线程。

1.任务:将大量(大约5G)一目录下有日期规则命名的html文件按照年月日三个层次目录存放,目的是为了提高文件检索效率。

2.具体实现:开启10个线程 将文件拷贝到目标文件夹;不符合要求的文件拷贝到别处;记录错误信息和不符合要求的信息;循环判断状态 执行完毕给出提示。

3.开始设想和后来出现问题:

开了10个线程 处理所有文件,如果一文件已在目标文件下存在 则不处理,但是线程间几乎是同时进行的 这样就会报错"该文件已被另一线程占用"。这样处理是不行的 于是就改善了。让每个线程按顺序处理固定条数的文件,则每个线程不会处理处理同个文件。

4.代码实现:

 声明变量

    /    /App.config 配置路径        //源文件路径        string sourcePath = ConfigurationManager.AppSettings["sourcePath"];        //目标路径        string toPath = ConfigurationManager.AppSettings["toPath"];        //错误文件存放路径        string errorLogPath = "\\log";        //文件总数        int totalFileCount;        //复制文件数        private int operaFileCount;        //未复制文件数        int notCopyFileCount;        //文件名称过长文件数        int longNameFileCount;        //线程数        int threadCount = 10;

 

将所有文件名称进行分批处理 分页方法
        /// <summary>        /// 将所有文件名称进行分页        /// </summary>        /// <param name="PageSize">每页条数</param>        /// <param name="CurPage">当前页</param>        /// <param name="objs">集合</param>        /// <returns></returns>        public static List<string> QueryByPage(int PageSize, int CurPage, List<string> objs)        {            return objs.Take(PageSize * CurPage).Skip(PageSize * (CurPage - 1)).ToList();        }              public class Page        {            public int pageIndex { get; set; }            public int pageCount { get; set; }            public int pageSize { get; set; }            public List<string> list { get; set; }        }

拷贝文件的方法

        #region DoWork方法        private string errorFieName;        public void DoWork(object obj)        {            Object locker = new object();            Page page = (Page)obj;            Console.WriteLine(ThreadName());            int PageSize = page.pageSize;            int CurPage = page.pageIndex;            var grouList = QueryByPage(PageSize, CurPage, page.list);            foreach (string file in grouList)            {                string fileName = Path.GetFileName(file);                errorFieName = fileName;                if (fileName != null && fileName.Length != 18)                {                    Console.WriteLine(fileName + "文件名称不符合要求!");                    CopyErrorFile(fileName);                    Write(fileName + "文件名称不符合要求!");                    continue;                }                //Console.WriteLine(fileName.Length);                try                {                    //截取文件名称 源文件名称规则 ABC2014200...html 意思是:2014年第200天 可以推算出年月日                    string subYearMonth = fileName.Substring(3, 5);                    int yearNum = 0;                    int dayNum = 0;                    int.TryParse(subYearMonth.Substring(0, 2), out yearNum);                    int.TryParse(subYearMonth.Substring(2, 3), out dayNum);                    int.TryParse("20" + yearNum, out yearNum);                    if (yearNum < 1 || dayNum < 1)                    {                        Console.WriteLine(fileName + "文件名称不符合要求!");                        CopyErrorFile(fileName);                        //Write(fileName + "文件名称不符合要求!");                        continue;                    }                    //声明日期                    DateTime date = new DateTime();                    date = date.AddYears(yearNum).AddYears(-1);                    date = date.AddDays(dayNum).AddDays(-1);                    string fullSavePath = string.Format("{0}\\{1}\\{2}\\{3}\\", toPath, yearNum, date.Month, date.Day);                    if (!Directory.Exists(fullSavePath))                    {                        Directory.CreateDirectory(fullSavePath);                    }                    lock (fullSavePath)                    {                        File.Copy(file, fullSavePath + fileName, true);                        operaFileCount++;                        //Write("处理完成:" + fileName);                    }                }                catch (Exception ex)                {                    Console.WriteLine("文件名称:" + errorFieName + "处理错误:" + ex.Message);                    Write(ex.Message);                    throw new Exception(ex.Message);                }            }        }        #endregion    

循环执行线程

        public void CopyFile()        {            //开始方法时删除上次记录            DeleteLog();            List<Thread> threadList = new List<Thread>(); ;            for (int i = 0; i < threadCount; i++)            {                try                {                    if (!Directory.Exists(toPath))                    {                        Directory.CreateDirectory(toPath);                    }                    //string[] fileNames = Directory.GetFileSystemEntries(sourcePath);                    string[] fileNames = Directory.GetFiles(sourcePath);                    var fileNameList = fileNames.ToList();                    totalFileCount=fileNames.Length;                                       //共threadCount个线程 每个线程执行oneThreadNameCount条                    int oneThreadNameCount = (int)Math.Ceiling(((double)fileNames.Length) / threadCount);                    Page page;                    //当前执行的线程                    page = new Page { pageIndex = i + 1, pageSize = oneThreadNameCount, list = fileNameList };                    threadList.Add(new Thread(new ParameterizedThreadStart(DoWork)));                    threadList[i].Start(page);                    threadList[i].Name = i + "_thread";                }                catch (Exception ex)                {                    Console.WriteLine("错误信息:" + ex.Message);                    Write(ex.Message);                }            }            //判断线程执行情况            bool isRanning;            bool isComplete = false;            while (!isComplete)            {                //2秒判断一次                Thread.Sleep(2000);                isRanning = false;                foreach (var item in threadList)                {                    if (item.ThreadState == ThreadState.Running)                    {                        isRanning = true;                    }                }                if (!isRanning)                 {                     isComplete = true;                    Console.WriteLine();                    Console.WriteLine("处理结果 共" + totalFileCount+";已处理:"+operaFileCount);                    Console.WriteLine("不符合要求:" + notCopyFileCount + "(已拷贝到errorfile文件夹);无法拷贝名称过长文件:" + longNameFileCount);                    Console.WriteLine("请查看文件夹" + toPath);                }            }            Console.ReadKey();        }

辅助方法

        #region copy不符合要求文件        private void CopyErrorFile(string fileName)        {            //实际长度超过222报错,并且无法抛出异常            if (fileName.Length > 180)            {                longNameFileCount += 1;                Write(fileName + "名称过长!");                return;            }            notCopyFileCount += 1;            string strErrorPath  =toPath+"\\errorfile";            if(!Directory.Exists(strErrorPath)) Directory.CreateDirectory(strErrorPath);            File.Copy(sourcePath+"\\"+fileName, toPath+"\\errorfile\\" + fileName, true);        }        #endregion        private void DeleteLog()        {            try            {                if (!Directory.Exists(toPath + errorLogPath)) return;                foreach (string var in Directory.GetFiles(toPath + errorLogPath))                {                    File.Delete(var);                }            }            catch            {                //Directory.CreateDirectory(toPath + errorLogPath);            }        }        private void Write(string message)        {            object obj = new object();            lock (obj)            {                try                {                    string logPath=toPath+errorLogPath;                    if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath);                    Thread primaryThread = Thread.CurrentThread;                    //当前线程名称                    string threadName = primaryThread.Name;                    using (TextWriter sw = new StreamWriter(logPath+"\\"+ThreadName()+"_error.txt", true))                    {                        sw.WriteLine("错误信息:" + message);                        sw.Dispose();                    }                                   }                catch (Exception ex)                {                    Console.WriteLine("错误信息:" + ex.Message);                }            }        }

 

多线程处理大量文件