Async和Backgroundworker组合

时间:2013-12-08 23:06:34

标签: c# wpf asynchronous backgroundworker

我在WPF中有以下代码,我的问题是:

  1. 如何使用并行任务和后台工作者更新我的文本框?我知道我的错误是在txtResult1.text个事件中写txtResult2.texttxtResult3.textworker_DoWork。但是如何解决这个问题?
  2. 代码需要ReadLineAsyncWriteLineAsync吗?
  3. 这是我的代码:

        public MainWindow()
        {
            InitializeComponent();
        }
        private readonly BackgroundWorker worker = new BackgroundWorker();
        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            worker.DoWork +=worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            worker.WorkerReportsProgress = true;
            worker.RunWorkerAsync();
        }
    
        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Done");
        }
    
        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            Parallel.Invoke(() =>
                        {  Task.Run(() => txtResult1.text = ReadCharacter("C:\\File 1.txt")); },
                        () =>
                        { Task.Run(()  => txtResult2.text = ReadCharacter("C:\\File 2.txt")); },
                        () =>
                        { Task.Run(()  => txtResult3.text = ReadCharacter("C:\\File 3.txt")); }
                        );
    
        }
    
        private string ReadCharacter(string inputFile)
        {
            string result; string TID;           
            using (StreamReader sr =new StreamReader(inputFile))
            using (StreamWriter sw = new StreamWriter(string.Format("{0}--Out.txt", inputFile.Replace(".txt",""))))
            {
                var sb = new StringBuilder();
    
                TID = "Thread ID: "+ Thread.CurrentThread.ManagedThreadId.ToString();
                sb.AppendLine(TID);
                sb.AppendLine("T Start : " + DateTime.Now.ToLongTimeString() + "." + 
                        DateTime.Now.Millisecond).ToString();
                while (!sr.EndOfStream)
                {
                    result = sr.ReadLine();
                    sw.WriteLine(result);
                };
    
                return
                sb.AppendLine("T Stop : "+DateTime.Now.ToLongTimeString() + "." + 
                        DateTime.Now.Millisecond).ToString();
            } 
        }
    

3 个答案:

答案 0 :(得分:1)

您的方法存在一些问题:

    如果您已使用Task.Run ,则
  1. Parallel.Invoke不会向您购买任何东西 如果你想做并发I / O ,那么
  2. Parallel是错误的解决方案 - Parallel用于并行CPU绑定操作。
  3. BackgroundWorker在这一点上几乎是遗产。我有blog series that shows how Task.Run is better than BackgroundWorker
  4. 另外,正如@HansPassnat指出的那样,你几乎肯定会想要并发磁盘访问,因为这通常会大大减慢你的速度。

    但如果你真的想这样做,我建议采用async方法:

    private async void btnStart_Click(object sender, RoutedEventArgs e)
    {
      var progress = new Progress<string>(value => { txtResult1.Text = value; });
      await Task.WhenAll(ReadCharacterAsync("C:\\File 1.txt", progress),
          ReadCharacterAsync("C:\\File 2.txt", progress),
          ReadCharacterAsync("C:\\File 3.txt", progress));
      MessageBox.Show("Done");
    }
    
    private async Task ReadCharacterAsync(string inputFile, IProgress<string> progress)
    {
        string result; string TID;
        using (var inFile = new FileStream(inputFile, FileMode.Open, FileAccess.Read, FileShare.None, 8192, useAsync: true))
        using (var outFile = new FileStream(string.Format("{0}--Out.txt", inputFile.Replace(".txt","")), FileMode.Create, FileAccess.ReadWrite, FileShare.None, 8192, useAsync: true))
        using (StreamReader sr =new StreamReader(inFile))
        using (StreamWriter sw = new StreamWriter(outFile))
        {
            var sb = new StringBuilder();
    
            TID = "Thread ID: "+ Thread.CurrentThread.ManagedThreadId.ToString();
            sb.AppendLine(TID);
            sb.AppendLine("T Start : " + DateTime.Now.ToLongTimeString() + "." + 
                    DateTime.Now.Millisecond).ToString();
            while (!sr.EndOfStream)
            {
                result = await sr.ReadLineAsync();
                await sw.WriteLineAsync(result);
            };
    
            progress.Report(
            sb.AppendLine("T Stop : "+DateTime.Now.ToLongTimeString() + "." + 
                    DateTime.Now.Millisecond).ToString());
        } 
    }
    

答案 1 :(得分:0)

在WPF中,您必须使用Dispatcher访问其他线程的控件:

 Parallel.Invoke(() =>
                {  Task.Run(() => this.Dispatcher.BeginInvoke(new Action(() => 
                   txtResult1.Text += ReadCharacter("C:\\File 2.txt")); },
                )

答案 2 :(得分:0)

您必须在处理事件的同一线程上设置文本框。您可以通过Task的{​​{1}}方法或在下面显示的方法中使用async / await(或其他解决方案中描述的.ContinueWith(...)方法)来执行此操作:

Dispatcher.BeginInvoke

用法:

private async Task GetDataAndSetTextBoxAsync( TextBox textBox, Func<char> dataMethod )
{
    var task = Task.StartNew( taskMethod );

    await task;

    // this will run on thread that called this method
    // in a WPF application
    textBox.Text = task.Result;
}