Async File Copy in C#






4.33/5 (2 votes)
Async file copy with read/ write threads.
Introduction
This class makes an async file copy. It uses a Buffer and makes cyclic reads.
Background
The code is useful in case of signature arrays to be checked at run-time.
Using the code
To start the copy call the StartCopy
function.
/// <summary>
/// Async File Copy
/// It seems the 32bit values work with atomic writes
/// </summary>
class FileCopy
{
//const string file = @"d:\AppSettings.dat";
#region KeepThem10MultiplyToAvoidDoubleCopy
const int sizeBufferCopy= 10485760; //100 MB Size of the memory buffer
const int sizeCyclicRead = 1048576; //10 MB
#endregion
private UInt64 indRd, indWr, nrReadings, nrWritings, nrReadsWrites, nrBytesWereRead;
private bool finishedRd= false, startCopy = true;
byte[] BufferCopy; //reference only, will be assign by the ReadBytes
private string destinationFile, sourceFile;
public FileCopy(string d, string s)
{
//BufferCopy = new byte[sizeBufferCopy];
destinationFile = d; sourceFile = s;
}
/// <summary>
/// Copy
/// </summary>
/// <returns></returns>
public bool StartCopy()
{
bool retValue = false;
//improve check the validity of the file names
if ((destinationFile != string.Empty) && (sourceFile != string.Empty))
{
Thread newThreadCopy = new Thread(ReadWorker);
Thread newThreadWrite = new Thread(WriteWorker);
newThreadCopy.Start(); //start to read Async to BufferCopy
newThreadWrite.Start(); //start to write Async from BufferCopy
retValue = true;
}
return retValue;
}
/// <summary>
/// WriteWorker
/// </summary>
private void WriteWorker()
{
//const string fileName = @"f:\AppSettingsCopy.dat";
UInt16 SleepTime = 10;
UInt64 nrBytesWriten = sizeCyclicRead;
do
{
Thread.Sleep(10);
} while (indRd == 0); //Wait for reading to begin
using (BinaryWriter writer = new BinaryWriter(File.Open(destinationFile, FileMode.Create)))
{
//check int conversion
do
{
if ((finishedRd == false) && (nrWritings >= (nrReadings - 1)))
//prevent writting beyond readIndex, improve with a better condition
{
Thread.Sleep(SleepTime);
SleepTime += 10; //increase 100ms
//keep sleeping until Read index progress
}
else
{
if (indWr >= sizeBufferCopy)
{
indWr = 0;
}
//last incomplete reading
if (nrWritings == nrReadsWrites - 1)
{
nrBytesWriten = nrBytesWereRead;
}
writer.Write(BufferCopy, (Int32)indWr, (Int32) nrBytesWriten);
indWr += sizeCyclicRead;
++nrWritings;
}
} while (nrWritings < nrReadsWrites);
} //end using
}
/// <summary>
/// Thread to Read the file
/// </summary>
private void ReadWorker()
{
FileStream f = File.Open(sourceFile, FileMode.Open);
UInt64 Length = (UInt64)f.Length, pos = 0;
BufferCopy = new byte[sizeBufferCopy];
nrReadsWrites = Length / sizeCyclicRead + 1;
UInt16 SleepTime = 10;
using (BinaryReader binR = new BinaryReader(f))
{
while(pos < Length)
{
startCopy = false;
if ((startCopy) || (indRd >= (indWr + sizeBufferCopy - 1)))
//the size of the cyclicc buffer is 10 multiple of the bigger BufferCopy
{
//pause the Read thread, read could be faster
//let the buffer to read more in advance
Thread.Sleep(SleepTime);
SleepTime += 10; //increase 100ms
//keep sleeping until Writes index progress
}
else
{
//read cicly 1024 bytes, hopefully ReadBytes uses the same buffer for consecutive readings
byte[] BufferRd = binR.ReadBytes(sizeCyclicRead);
nrBytesWereRead = (UInt64) BufferRd.Length; //still in int size limits
//check if the indRd goes out range, begin writing from the begining,
// Avoid 2 copies by keeping the indexes multiple by 10
if (indRd >= sizeBufferCopy)
{
indRd = 0;
}
//improve - without copy !!!
Array.Copy(BufferRd, 0, BufferCopy, (Int64)indRd, (Int64)nrBytesWereRead);
indRd += nrBytesWereRead;
pos += nrBytesWereRead;
++nrReadings;
}
} //endwhile
} //end using
//Reading the file Done
finishedRd= true;
}
}