Search code examples
c#wpfasynchronousbackgroundworker

Async and Backgroundworker combination


I have following code in WPF, my problems are :

  1. How to update my textbox with parallel task and backgroundworker ?. I know my mistake is writing txtResult1.text , txtResult2.text, txtResult3.text inside worker_DoWork events. But how to fix this ?
  2. Do I need ReadLineAsync and WriteLineAsync for code ?

Here is my code:

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

Solution

  • There are several problems with your approach:

    1. Task.Run doesn't buy you anything if you're already using Parallel.Invoke.
    2. Parallel is the wrong solution if you want to do concurrent I/O - Parallel is for parallel CPU-bound operations.
    3. BackgroundWorker is pretty much legacy at this point. I have a blog series that shows how Task.Run is better than BackgroundWorker.

    Also, as @HansPassnat pointed out, you almost certainly do not want concurrent disk access since that will usually slow you down considerably.

    But if you really want to do it, I'd recommend an async approach:

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