Click here to Skip to main content
15,884,237 members
Articles / Programming Languages / C#
Tip/Trick

Async File Copy in C#

Rate me:
Please Sign up or sign in to vote.
4.33/5 (2 votes)
18 Jan 2013CPOL 35.1K   6   2
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)


Written By
Software Developer (Junior)
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
John Brett20-Jan-13 22:56
John Brett20-Jan-13 22:56 
GeneralRe: My vote of 3 Pin
Drazen Dotlic26-Jan-13 7:18
Drazen Dotlic26-Jan-13 7:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.