C#实现多线程读取FastCopy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
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,布布扣,bubuko.com

C#实现多线程读取FastCopy

上一篇:编程理论之 —— 多线程编程的同步互斥


下一篇:一段好玩的Python代码解读