【问题标题】:Is it possible to "launch" a copy of a (large) file without waiting for the result?是否可以在不等待结果的情况下“启动”(大)文件的副本?
【发布时间】:2017-04-27 13:55:15
【问题描述】:

我认为这在标题中很明显;我想在不等待结果的情况下复制文件。

功能上我想要这个:

    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 !");
    }

基本上我的主线程在几毫秒后被释放。 我尝试这样做:

    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);
            }
        }
    }

我的 mytestfile.fil 是在我的目标文件夹中创建的,但它的大小是 0kb。

问候,

【问题讨论】:

  • 在您的主程序中,您必须等待任务完成(Task.Wait(...))。否则控制台退出并终止工作线程
  • 那么...第一种方法有什么问题(使用cmd.exeProcess.Start)?如果这就是你想要的,那它对你不起作用怎么办?
  • @samir 是的,但我不想等待 ;-)
  • 您无法关闭该进程,并且您在同一进程中启动的线程仍然在运行。进程拥有线程,当进程结束时它们将被终止。这就是为什么您必须按照我在回答中的建议以某种方式保持流程(应用程序)的活力。
  • 除非在异步调用之后发生处理,否则主线程将退出,并带走孩子。如果您在文件写入完成之前无法继续处理,我认为不需要发生这种情况。您将无法避免等待童工...

标签: c# asynchronous task-parallel-library xcopy


【解决方案1】:

基本上我的主线程在几毫秒后被释放。我试着这样做

您必须以某种方式保持该过程的活力。最简单的方法——如果你真的必须在它自己的线程上进行文件复制——是在 foreground 线程上执行你的 Copy 方法:

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);
            }
        }
    }
}

...或者您可以在主线程上执行它,因为您的应用程序似乎没有其他任何东西:

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);
            }
        }
    }
}

第三种选择是使您的方法异步并等待它在 Main 方法中同步完成:

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);
            }
        }
    }
}

请注意,除非您实际等待,否则不应使用异步 CopyToAsync 方法。不要混合同步和异步代码:)

【讨论】:

  • 我试过你的代码是一样的......文件被创建但没有被复制。 (0kb)
  • 还有线程是遗留代码不要使用这个!我不知道为什么被赞成!
  • 哦,你必须使用同步CopyTo方法或者等待CopyToAsync方法。我编辑了我的答案。
【解决方案2】:

确定不止一个错误。

  • 您的程序在复制完成之前退出。你需要这样的东西:
  • 没有等待 CopyToAsync

    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);
            }
        }
    }
    

    }

【讨论】:

  • async Task 不是async void
  • async void 适用于控制台应用程序:stackoverflow.com/questions/9208921/…。他也可以像在 cmets 中提到的 Stephen Cleary 一样编写 Task.Wait()。我认为这个答案应该是正确的。
  • 好的测试和工作。还更改了异步主目录,因此您不需要库
【解决方案3】:

您的 Main 方法在任务完成之前返回,因此您的应用程序结束。

【讨论】:

    【解决方案4】:

    这似乎是XY problem 的一个实例。

    请记住,操作系统/框架基本上是为您的进程提供了自己的“空间”来运行,因此如下:

    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);
            }
        }
    }
    

    在性能方面与只是做没有什么不同

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

    由于您的控制台应用程序基本上除了复制文件之外什么都不做,额外的线程只会增加开销。

    另外,如果您在 Copy 方法中使用异步操作,您应该声明您的方法 async Task 而不是 void,否则您不能在 Copy 方法中使用 await 关键字或等待Copy 方法在代码的其他地方完成。所以:

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

    正如所写,这有一个明显的竞争条件,可能无法正常工作,因为DestinationStreamSourceStream 可能在SourceStream.CopyToAsync(DestinationStream) 实际完成之前被处理掉。你需要做的

    await SourceStream.CopyToAsync(DestinationStream);
    

    在你的方法中防止这种情况发生。

    如果您使用

    启动复制操作
    System.Diagnostics.Process.Start(...);
    

    从启动它的程序的角度来看,您已经有效地异步运行它,因为启动它的程序在继续之前不会等待进程完成。顺便说一句,我假设该控制台应用程序会做一些事情,而不仅仅是启动进程,对吗? (如果不是,那似乎有点多余)。

    【讨论】:

    • 当然可以。只是为了展示我想要的东西
    • 最后,这可能不是这样做的好方法。可能我会使用队列系统(MSMQueue 或其他)。
    • @Florian 如果您对此感兴趣,我有一个代码示例here 值得一看。
    猜你喜欢
    • 2019-12-02
    • 1970-01-01
    • 2016-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多