C#实现多线程读取FastCopy

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Media;
using System.Windows.Forms;
using System.Runtime.Remoting.Contexts;
using System.Diagnostics;
    
namespace FastCopy
{
    public class FastCopy
    {
        private const short FILE_ATTRIBUTE_NORMAL = 0x80;
        private const short INVALID_HANDLE_VALUE = -1;
        private const uint GENERIC_READ = 0x80000000;
        private const uint GENERIC_WRITE = 0x40000000;
        private const uint CREATE_NEW = 1;
        private const uint CREATE_ALWAYS = 2;
        private const uint OPEN_EXISTING = 3;
        private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
        private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
        private const uint FILE_SHARE_READ = 0x00000001;
        private const uint FILE_SHARE_WRITE = 0x00000002;
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern SafeFileHandle CreateFile(string IpFileName, uint dwDesiredAccess,
            uint dwShareMode, IntPtr IpSecurityAttributes, uint dwCreationDisposition,
            uint dwFlagsAndAttributes, IntPtr hTemplateFile);
        private int _ThreadNum;
        private Thread[] CopyThread;
        private long ReadBufferSize = 1024 * 1024 * 16;
        public long TotalReadCount = 0;
        public long AverageCopySpeed;
        public int ProgressBarValue = 0;
        private DateTime start;
        private FileInfo SourceFileInfo;
        private string _DestFileName;
        private string _SourceFileName;
        private bool _IsUsingSystemBuff;
        public delegate void CopyFinished(string IsFinish);
        private bool[] isfirst;
        public event CopyFinished CopyF;
        private bool WaitingEnd = true;
        private DateTime WaitTime;
        private int ThreadExitCout = 0;
        private object ThreadLock = new object();
        /// <summary>
        /// 执行复制函数,线程数如果大于8将按照最多8线程复制
        /// </summary>
        /// <param name="SourceFileName">源文件全路径</param>
        /// <param name="DestFileName">目标文件全路径</param>
        /// <param name="IsUsingSystemBuff">是否使用系统缓存,不适用系统缓存的复制速度将远大于使用系统缓存的复制速度</param>
        /// <param name="ThreadNum">复制线程数</param>
        /// <param name="IsSynchronous">true是同步,false是异步</param>
        /// <param name="WaitMilliseconds">同步等待时间</param>
        public void ExeCopy(string SourceFileName, string DestFileName, bool IsUsingSystemBuff, int ThreadNum, bool IsSynchronous, double WaitMilliseconds)
        {
            Console.WriteLine("开始时间:"+DateTime.Now.ToString("hh:mm:ss"));
            try
            {
                SourceFileInfo = new FileInfo(SourceFileName);
                _DestFileName = DestFileName;
                _SourceFileName = SourceFileName;
                _IsUsingSystemBuff = IsUsingSystemBuff;
                //if (SourceFileInfo.Exists)
                //{
                    //小文件使用系统复制File.Copy
                    if (SourceFileInfo.Length > 0 && SourceFileInfo.Length < 100 * 1024 * 1024)
                    {
                        File.Copy(SourceFileName, DestFileName);
                    }
                    else//大于100M文件才使用FastCopy
                    {
                        if (ThreadNum > 0)
                        {
                            //建立于源文件同样大小的目标空文件
                            if (initFile(SourceFileName, DestFileName))//如果建立或者覆盖文件成功
                            {
                                //打开目标文件
    
                                //线程数量限制
                                ThreadNum = ThreadNum > 8 ? 8 : ThreadNum;
                                _ThreadNum = ThreadNum;
                                CopyThread = new Thread[ThreadNum];
                                isfirst = new bool[ThreadNum];
                                if (ThreadNum == 1)//执行单线程复制
                                {
                                    ThreadParams threadParam = new ThreadParams();
                                    threadParam.StartPosition = 0;
                                    threadParam.ReadLength = SourceFileInfo.Length;
                                    threadParam.start = DateTime.Now;
                                    CopyThread[0] = new Thread(new ParameterizedThreadStart(ExeThreadCopy));
                                    CopyThread[0].Start(threadParam);
                                }
                                else//执行多线程复制
                                {
                                    long parts = (long)_ThreadNum;
                                    long StartPosition = 0;
                                    long len = SourceFileInfo.Length;
                                    long last = SourceFileInfo.Length % parts;
                                    len = len - last;
                                    long PartLength = len / parts;
                                    PartLength = PartLength - PartLength % 512;
                                    last = SourceFileInfo.Length - parts * PartLength;
                                    start = DateTime.Now;//记录开始时间
                                    for (int i = 0; i < ThreadNum; i++)
                                    {
                                        CopyThread[i] = new Thread(new ParameterizedThreadStart(ExeThreadCopy));
                                        CopyThread[i].Name = i.ToString();
                                        if (i == ThreadNum - 1)
                                        {
                                            ThreadParams threadParam = new ThreadParams();
                                            threadParam.StartPosition = StartPosition;
                                            threadParam.ReadLength = PartLength + last;
                                            threadParam.start = start;
                                            CopyThread[i].Start(threadParam);
                                        }
                                        else
                                        {
                                            ThreadParams threadParam = new ThreadParams();
                                            threadParam.StartPosition = StartPosition;
                                            threadParam.ReadLength = PartLength;
                                            StartPosition += PartLength;
                                            threadParam.start = start;
                                            CopyThread[i].Start(threadParam);
                                        }
                                    }
                                }
                                    
                            }
                        }
                        else
                            throw new Exception("线程数不能小于1");
                    }
                //}
                //else
                //    throw new Exception("打开源文件失败!");
                //等待线程结束
                if (IsSynchronous)
                {
                    WaitTime = DateTime.Now;
                    WaitForEnd(WaitMilliseconds);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            Console.WriteLine("结束时间:" + DateTime.Now.ToString("hh:mm:ss"));
        }
        private void WaitForEnd(double WaitMilliseconds)
        {
            while (ThreadExitCout < _ThreadNum)
            {
                Thread.Sleep(100);
                TimeSpan ts = DateTime.Now.Subtract(WaitTime);
                if (ts.TotalMilliseconds > WaitMilliseconds)
                {
                    throw new Exception("文件拷贝超时异常");
                    break;
                }
            }
        }
        private bool initFile(string SourceFileName, string DestFileName)
        {
            try
            {
                FileInfo SourceFileInfo = new FileInfo(SourceFileName);
                FileInfo DestFileInfo = new FileInfo(DestFileName);
    
                if (DestFileInfo.Exists)
                {
                    DestFileInfo.Delete();
                }
                Process p = new Process();
                p.StartInfo.FileName = "fsutil";
                p.StartInfo.Arguments = "file createnew " + DestFileName + " " + SourceFileInfo.Length.ToString();
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardError = true;
                p.StartInfo.CreateNoWindow = true;
                p.Start();
                p.WaitForExit(1000 * 60 * 2);
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        private void ExeThreadCopy(object obj)
        {
            ThreadParams param = (ThreadParams)obj;
                
            SafeFileHandle SafeFile_SourceFile = CreateFile(_SourceFileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
                                                            OPEN_EXISTING, _IsUsingSystemBuff ? 0 : FILE_FLAG_NO_BUFFERING, IntPtr.Zero);
            SafeFileHandle SafeFile_DestFile = CreateFile(_DestFileName, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero,
                                                          OPEN_EXISTING, _IsUsingSystemBuff ? 0 : (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH), IntPtr.Zero);
    
            FileStream SourceFileStream = new FileStream(SafeFile_SourceFile, FileAccess.Read);
            FileStream DestFileStream = new FileStream(SafeFile_DestFile, FileAccess.Write);
            if (param.StartPosition != 0)
            {
                SourceFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
                DestFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
            }
            BinaryReader SourceFileReader = new BinaryReader(SourceFileStream);
            BinaryWriter DestFileWriter = new BinaryWriter(DestFileStream);
    
            long ThreadTotalReadCount = 0;
            long ThreadOneTimeReadCount = 0;
            long ReadCount = 0;
            bool IsEndPart = false;
            byte[] ReadBuff = new byte[ReadBufferSize];
            int ThreadName = int.Parse(Thread.CurrentThread.Name);
            while (ThreadTotalReadCount < param.ReadLength)
            {
                //计算每次应该读取流的长度,因为在每部分的最后一点不一定是ReadBufferSize大小?如果不设置流的读取长度,有可能在每部分最后一次读取越界。读到下一部分的内容。
                Console.WriteLine(Thread.CurrentThread.Name);
                ReadCount = param.ReadLength - ThreadTotalReadCount < ReadBufferSize ? param.ReadLength - ThreadTotalReadCount : ReadBufferSize;
                if (ReadCount % 512 == 0)//不是最后一部分的最后一点
                {
                    IsEndPart = false;
                }
                else
                {
                    IsEndPart = true;
                }
    
                if (IsEndPart)
                {
                    FileStream SourceFileLastStream = new FileStream(_SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                    FileStream DestFileLastStream = new FileStream(_DestFileName, FileMode.Open, FileAccess.Write, FileShare.Write);
                    BinaryReader SourceFileReadLast = new BinaryReader(SourceFileLastStream);
                    BinaryWriter DestFileWriteLast = new BinaryWriter(DestFileLastStream);
                    SourceFileLastStream.Seek(SourceFileStream.Position, SeekOrigin.Begin);
                    DestFileLastStream.Seek(DestFileStream.Position, SeekOrigin.Begin);
                    byte[] LastBuff = new byte[ReadCount];
                    ThreadOneTimeReadCount = SourceFileReadLast.Read(LastBuff, 0, (int)ReadCount);
                    DestFileWriteLast.Write(LastBuff, 0, (int)ReadCount);
                    try
                    {
                        SourceFileReadLast.Close();
                    }
                    catch { }
                    try
                    {
                        DestFileWriteLast.Close();
                    }
                    catch { }
                    try
                    {
                        SourceFileLastStream.Close();
                    }
                    catch { }
                    try
                    {
                        DestFileLastStream.Close();
                    }
                    catch { }
                    if (CopyF != null)
                    {
                        CopyF("复制完成");
                    }
                }
                else
                {                  
                    ThreadOneTimeReadCount = SourceFileReader.Read(ReadBuff, 0, (int)ReadCount);                                      
                    DestFileWriter.Write(ReadBuff, 0, (int)ReadCount);
                }
                TotalReadCount += ThreadOneTimeReadCount;
                ThreadTotalReadCount += ThreadOneTimeReadCount;
                TimeSpan ts = DateTime.Now.Subtract(param.start);
                AverageCopySpeed =  TotalReadCount / (long)ts.TotalMilliseconds * 1000 / (1024 * 1024);
                ProgressBarValue =(int)(TotalReadCount * 100 / SourceFileInfo.Length);
                WaitTime = DateTime.Now;
            }
            try{
                SourceFileReader.Close();}
                catch { };
            try{
                DestFileWriter.Close();}
                catch { };
            try{
                SourceFileStream.Close();}
                catch { };
            try{
                DestFileStream.Close();}
                catch { };
            try{
                SafeFile_SourceFile.Close();}
                catch { };
            try{
                SafeFile_DestFile.Close();}
                catch { };
                lock (ThreadLock)
                {
                    ThreadExitCout += 1;
                }
        }
    
        private void ExcNormalCopy(object obj)
        {
            ThreadParams param = (ThreadParams)obj;
            FileStream SourceFileStream = new FileStream(_SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            FileStream DestFileStream = new FileStream(_DestFileName, FileMode.Open, FileAccess.Write, FileShare.Write);
            BinaryReader SourceFileReader = new BinaryReader(SourceFileStream);
            BinaryWriter DestFileWriter = new BinaryWriter(DestFileStream);
            SourceFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
            DestFileStream.Seek(param.StartPosition, SeekOrigin.Begin);
            long ThreadTotalReadCount = 0;
            long ThreadOneTimeReadCount = 0;
            long ReadCount = 0;
            byte[] buff = new byte[ReadBufferSize];
            while (TotalReadCount < param.ReadLength)
            {
                ReadCount = param.ReadLength - ThreadTotalReadCount >= ReadBufferSize ? ReadBufferSize : param.ReadLength - ThreadTotalReadCount;
                ThreadOneTimeReadCount = SourceFileReader.Read(buff, 0, (int)ReadCount);
                DestFileWriter.Write(buff, 0, (int)ReadCount);
                TimeSpan ts = DateTime.Now.Subtract(param.start);
                TotalReadCount += ThreadOneTimeReadCount;
                ThreadTotalReadCount += ThreadOneTimeReadCount;
                AverageCopySpeed = TotalReadCount / (long)ts.TotalMilliseconds * 1000 / (1024 * 1024);
                ProgressBarValue = (int)(TotalReadCount * 100 / SourceFileInfo.Length);
            }
            SourceFileReader.Close();
            DestFileWriter.Close();
            SourceFileStream.Close();
            DestFileStream.Close();
        }
        public void AbortAllThread()
        {
            for (int i = 0; i < _ThreadNum; i++)
            {
                if (CopyThread[i].IsAlive)
                {
                    CopyThread[i].Abort();
                }
            }
        }
    }
    public class ThreadParams
    {
        public long StartPosition;
        public long ReadLength;
        public DateTime start;
    }
}


知识共享许可协议
《C#实现多线程读取FastCopy》常伟华 创作。
采用 知识共享 署名-相同方式共享 3.0 中国大陆 许可协议进行许可。
  • 多说评论
  • 签名
  • 新浪微博
  • 默认评论
  • Tab Header 5

0 条评论 / 点击此处发表评论

Tab Content 5

开发技术


开发平台和工具

sitemap     9779.59ms