3

I think it's pretty obvious in the title ; I want to copy a file without waiting for the result.

Functionaly I want this :

    static void Main(string[] args)
    {
        string strCmdText = @"/C xcopy c:\users\florian\desktop\mytestfile.fil p:\";
        System.Diagnostics.Process.Start("CMD.exe", strCmdText);
        Console.WriteLine("Finished !");
    }

Basically my main thread is released after few milliseconds. I try to do like this :

    static void Main(string[] args)
    {
        var t = Task.Run(() => Copy(@"c:\Users\florian\Desktop\mytestfile.fil", "p:"));
    }

    private static void Copy(string source, string destination)
    {
        using (FileStream SourceStream = File.Open(source, FileMode.Open))
        {
            using (FileStream DestinationStream = File.Create(destination + source.Substring(source.LastIndexOf('\\'))))
            {
                SourceStream.CopyToAsync(DestinationStream);
            }
        }
    }

My mytestfile.fil is created in my destination folder but its size is 0kb.

Regards,

Florian
  • 4,507
  • 10
  • 53
  • 73
  • 1
    In your main you'll have to wait for the Task to complete (Task.Wait(...)). Otherwise the console exits and terminates the worker thread – Samir Banjanovic Dec 12 '16 at 16:32
  • 1
    So... what's wrong with the first approach (using `cmd.exe` and `Process.Start`)? If that's what you want, what about it doesn't work for you? – J... Dec 12 '16 at 16:43
  • @samir Yes but I don't want wait ;-) – Florian Dec 12 '16 at 17:05
  • 1
    You cannot shut down the process and still have a thread that you started within the same process keep running. The process owns the threads and they will be terminated when the process ends. That's why you must keep the process (application) alive somehow as I suggested in my answer. – mm8 Dec 12 '16 at 17:47
  • Unless there's processing happening after the async call the main thread will exit, taking the child with it. I don't see the need for this to happen if you're processing can't proceed till the file write is complete. You won't be able to avoid waiting on the child worker... – Samir Banjanovic Dec 12 '16 at 18:13

4 Answers4

5

Basically my main thread is released after few milliseconds. I try to do like this

You must keep the process alive somehow. The easiest way to do this - if you actually have to do the file copy on a thread of its own - is to execute your Copy method on a foreground thread:

class Program
{
    static void Main(string[] args)
    {
        System.Threading.Thread thread = new System.Threading.Thread(()=> Copy(@"c:\Users\florian\Desktop\mytestfile.fil", "p:"));
        thread.Start();
    }

    private static void Copy(string source, string destination)
    {
        using (FileStream SourceStream = File.Open(source, FileMode.Open))
        {
            using (FileStream DestinationStream = File.Create(destination + source.Substring(source.LastIndexOf('\\'))))
            {
                SourceStream.CopyTo(DestinationStream);
            }
        }
    }
}

...or you could execute it on the main thread since your application doesn't seem to anything else:

class Program
{
    static void Main(string[] args)
    {
        string source = @"c:\Users\florian\Desktop\mytestfile.fil";
        string destination = "p";
        using (FileStream SourceStream = File.Open(source, FileMode.Open))
        {
            using (FileStream DestinationStream = File.Create(destination + source.Substring(source.LastIndexOf('\\'))))
            {
                SourceStream.CopyTo(DestinationStream);
            }
        }
    }
}

A third option is to make your method async and wait for it to complete synchronously in the Main method:

class Program
{
    static void Main(string[] args)
    {
        CopyAsync(@"c:\Users\florian\Desktop\mytestfile.fil", "p:").Wait();
    }

    private static async Task CopyAsync(string source, string destination)
    {
        using (FileStream SourceStream = File.Open(source, FileMode.Open))
        {
            using (FileStream DestinationStream = File.Create(destination + source.Substring(source.LastIndexOf('\\'))))
            {
                await SourceStream.CopyToAsync(DestinationStream);
            }
        }
    }
}

Note that you should not use the asyncronous CopyToAsync method unless you actiually await it. Don't mix synchronous and asynchronous code :)

mm8
  • 163,881
  • 10
  • 57
  • 88
4

OK more than one error.

  • Your program exits before the copy completes. you need something like:
  • CopyToAsync was not awaited

    class Program
    {
        private static void Main(string[] args)
        {
            var source = new CancellationTokenSource();
            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                source.Cancel();
            };
    
        try
        {
            MainAsync(args, source.Token).GetAwaiter().GetResult();
            return;
        }
        catch (OperationCanceledException)
        {
            return;
        }
    }
    
    static async Task MainAsync(string[] args, CancellationToken token)
    {
        var t = Task.Run(() => Copy(@"c:\test.txt", @"c:\dest\"));
        Console.WriteLine("doing more stuff while copying");
        await t;
        Console.WriteLine("Finished !");
    }
    
    private static void Copy(string source, string destination)
    {
        using (FileStream SourceStream = File.Open(source, FileMode.Open))
        {
            using (FileStream DestinationStream = File.Create(destination + source.Substring(source.LastIndexOf('\\'))))
            {
                SourceStream.CopyTo(DestinationStream);
            }
        }
    }
    

    }

Ewan
  • 1,261
  • 1
  • 14
  • 25
  • 4
    ``async Task`` not ``async void`` – Ehsan Sajjad Dec 12 '16 at 16:37
  • async void is okay for console application: http://stackoverflow.com/questions/9208921/cant-specify-the-async-modifier-on-the-main-method-of-a-console-app . Also he can just write Task.Wait() like Stephen Cleary mention in the comments. I think this answer should be the correct one. – mybirthname Dec 12 '16 at 16:41
  • ok tested and working. also changed the async main so you dont need a library – Ewan Dec 12 '16 at 16:52
2

It seems like this might be an instance of the XY problem.

Keep in mind that the OS/framework is basically giving your process its own "space" to run in, so the following:

static void Main(string[] args)
{
    var t = Task.Run(() => Copy(@"c:\Users\florian\Desktop\mytestfile.fil", "p:"));
}

private static void Copy(string source, string destination)
{
    using (FileStream SourceStream = File.Open(source, FileMode.Open))
    {
        using (FileStream DestinationStream = File.Create(destination + source.Substring(source.LastIndexOf('\\'))))
        {
            SourceStream.CopyToAsync(DestinationStream);
        }
    }
}

is no different in terms of performance than just doing

static void Main(string[] args)
{
    Copy(@"c:\Users\florian\Desktop\mytestfile.fil", "p:");
}

Since your console application fundamentally isn't doing anything other than copying the file, the extra thread just adds overhead.

Also, if you're using asynchronous operations inside the Copy method, you should declare your method async Task rather than void because otherwise you can't use the await keyword inside the Copy method or wait for the Copy method to complete elsewhere in code. So:

private static async Task Copy(string source, string destination)

As written, this has a blatant race condition and probably won't work correctly because DestinationStream and SourceStream may be disposed of before SourceStream.CopyToAsync(DestinationStream) has actually finished. You need to do

await SourceStream.CopyToAsync(DestinationStream);

inside your method to prevent this.

If you're launching the Copy operation with

System.Diagnostics.Process.Start(...);

you're already effectively running this asynchronously from the perspective of the program that launched it because the program that launched it doesn't wait for the process to complete before it continues. Incidentally, I assume that that console application will do something other than just launch the process, correct? (If not, it seems a little redundant).

Community
  • 1
  • 1
1

Your Main method returns before the Task has completed, so your application ends.

Owen Pauling
  • 11,349
  • 20
  • 53
  • 64