Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Async File Copy in C#

4.33/5 (2 votes)
18 Jan 2013CPOL 35.7K  
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.

C#
/// <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;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)