1

我正在尝试创建一个文件复制应用程序。我有一个 BackgroudWorker 做这项工作,到目前为止它工作正常。它是如何工作的,我有一个表单、源文件夹和目标文件夹字段以及一个复制按钮。Copy 按钮使用 BackgroundWorker 触发 Copy 操作。进度条得到更新等。现在我需要实现一个队列类型的操作。我需要添加其他源和目标复制操作并将其添加到队列中。我尝试使用以下内容:

Queue<MethodInvoker> MyQueue = new Queue<MethodInvoker>();

        MyQueue.Enqueue(new MethodInvoker(() =>CopyStuff(1)));
        MyQueue.Enqueue(new MethodInvoker(() =>CopyStuff(2)));

        MethodInvoker bb = MyQueue.Dequeue(); //(I Forgot this part)
        bb();
        bb = MyQueue.Dequeue();
        bb();

        
        

问题是,因为它是一个 BackgroundWorker,它不会等待第一个操作完成。关于如何解决这个问题的任何建议? 修复我的代码后,它可以工作,除了它在 UI 线程上运行,锁定控件。

更新1:

这可行,但在主线程上运行,用户在运行时无法使用控件:

        BlockingCollection<MethodInvoker> bCollection = new BlockingCollection<MethodInvoker>(boundedCapacity: 2);
        Task producerThread = Task.Factory.StartNew(() =>
        {
            for (int i = 0; i < 2; ++i)
            {

                bCollection.Add(CopyStuff);
            }

            bCollection.CompleteAdding();
        });

        foreach (MethodInvoker item in bCollection.GetConsumingEnumerable())
        {
            BeginInvoke(item);
        }

更新 2:

适用于控制台应用程序,但不适用于 Windows 窗体应用程序。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AsyncCopy
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        public class AsyncCopy
        {
            private static Queue<Action> MyQueue = new Queue<Action>();

            public async Task EnqueueAndCopy(Action[] actionList)
            {
                foreach (var action in actionList)
                {
                    MyQueue.Enqueue(action);
                }
                while (MyQueue.TryDequeue(out var copyAction)) //Here's the problem on Windows Form Applivcation
                {
                    //If the copyaction is itself async, await directly on the method here instead of running the action in a Task
                    await Task.Factory.StartNew(copyAction);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var asyncCopy = new AsyncCopy();
            //In a typical usage, this will call be awaited
            //In your case, make this call from a async method and call it from the UI thread
            asyncCopy.EnqueueAndCopy(
                new Action[] {
                () => CopyStuff (1),
                () => CopyStuff (2)
                }
            );

            //Loop to confirm the processing is asynchronous
            for (int i = 0; i <= 20; i++)
            {
                Console.WriteLine($"{i}");
                Thread.Sleep(300);
            }
        }
        //Copy process simulation
        static void CopyStuff(int i)
        {
            Console.WriteLine($"Copying {i}");
            Thread.Sleep(3000);
            Console.WriteLine($"Copied {i}");
        }
    }
}
4

1 回答 1

1

如果您需要在其他副本正在处理时异步排队复制过程,我会推荐一种生产者消费者模式。参考https://www.dotnetcurry.com/patterns-practices/1407/producer-consumer-pattern-dotnet-csharp

但是一个简单的异步等待也适用于您的情况

   using System;
   using System.Collections.Generic;
   using System.Threading.Tasks;
   using System.Threading;

   namespace stackoverflow {
   class Program {

    static void Main (string[] args) {
        var asyncCopy = new AsyncCopy ();
        //In a typical usage, this will call be awaited
        //In your case, make this call from a async method and call it from the UI thread
        asyncCopy.EnqueueAndCopy (
            new Action[] {
                () => CopyStuff (1),
                () => CopyStuff (2)
            }
        );

        //Loop to confirm the processing is asynchronous
        for(int i=0; i <= 20; i++)
        {
            Console.WriteLine($"{i}");
            Thread.Sleep(300);
        }
    }
    //Copy process simulation
    static void CopyStuff (int i) {
        Console.WriteLine ($"Copying {i}");
        Thread.Sleep(3000);
        Console.WriteLine ($"Copied {i}");
    }

}

public class AsyncCopy {
    private static Queue<Action> MyQueue = new Queue<Action> ();

    public async Task EnqueueAndCopy (Action[] actionList) {
        foreach (var action in actionList) {
            MyQueue.Enqueue (action);
        }
        while (MyQueue.TryDequeue (out var copyAction)) {
            //If the copyaction is itself async, await directly on the method here instead of running the action in a Task
            await Task.Factory.StartNew (copyAction);
        }
    }
}
}

更新

  using System;
  using System.Collections.Generic;
  using System.Threading;
  using System.Threading.Tasks;
  using System.Windows.Forms;

  namespace WindowsFormsApplication1
  {
   public partial class Form1 : Form
   {
    public Form1()
    {
        InitializeComponent();
    }

    private async Task CopyAsync(IEnumerable<Action> actionList)
    {
        foreach (var action in actionList)
        {
            await Task.Factory.StartNew(action);
        }
    }

    private async void button2_Click(object sender, EventArgs e)
    {
        await CopyAsync(
            new Action[]
            {
                () => CopyStuff(1),
                () => CopyStuff(2)
            });
    }

    //Copy process simulation
    static void CopyStuff(int i)
    {
        Thread.Sleep(3000);
        MessageBox.Show(string.Format("File Copied {0}", i));
    }
}
}
于 2020-06-28T21:11:28.067 回答