What is the simplest way to implement multithreading in c# to existing code

I have already implemented a functionnal application that parses 26 pages of html all at once to produce an xml file with data contained on the web pages. I would need to implement a thread so that this method can work in the background without causing my app to seems unresponsive.

Secondly, I have another function that is decoupled from the first one which compares two xml files to produce a third one and then transform this third xml file to produce an html page using XSLT. This would have to be on a thread, where I can click Cancel to stop the thread whithout crashing the app.

What is the easiest best way to do this using WPF forms in VS 2010 ?

I have chosen to use the BackgroundWorker.

BackgroundWorker implementation:

public partial class MainWindow : Window
    {
        private BackgroundWorker bw = new BackgroundWorker();

        public MainWindow()
        {
            InitializeComponent();
            bw.WorkerReportsProgress = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.LoadFiles();
        }

        private void btnCompare_Click(object sender, EventArgs e)
        {
            if (bw.IsBusy != true)
            {
                progressBar2.IsIndeterminate = true;
                // Start the asynchronous operation.
                bw.RunWorkerAsync();
            }

        }

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            StatsProcessor proc = new StatsProcessor();
            if (lstStatsBox1.SelectedItem != null)
                if (lstStatsBox2.SelectedItem != null)
                    proc.CompareStats(lstStatsBox1.SelectedItem.ToString(), lstStatsBox2.SelectedItem.ToString());
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            progressBar2.IsIndeterminate = false;
            progressBar2.Value = 100;
        }

I have started with the bgworker solution, but it seems that the bw_DoWork method is never called when btnCompare is clicked, I must be doing something wrong... I am new to threads.

Upvotes: 2

Views: 1035

Answers (3)

BrokenGlass
BrokenGlass

Reputation: 161002

If you're new to threading I think the easiest to start with would be using a BackgroundWorker:

  • It uses another thread: You can asynchronously perform the HTML parsing while your app remains responsive
  • It supports cancellation: You can can cancel the XML to HTML conversion

The BackgroundWorker is event-driven so it's much easier to get your ahead around if you're new to multi-threading. The .NET 4 Task library is much more flexible, but a little more involved to take advantage of especially with UI updates.

Example:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler((o, args) =>
{
    //Long running stuff here       
    Thread.Sleep(10000);
    string result = "Hi UI!";
    args.Result = result;
});

worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, args) =>
{
    if (args.Result != null)
    {
        SomeTextBox.Text = args.Result;
    }
});

worker.RunWorkerAsync();

Upvotes: 2

fbl
fbl

Reputation: 2920

Since you're using .NET 4 and VS2010, I would use the Task Parallel Library. Here's an example that shows the basics of how to background a task and marshal UI updates back to the GUI. I think you need to use a BlockingCollection to implement the second part.

There is a good blog post that talks about chaining tasks and supporting cancellation: http://blogs.msdn.com/b/csharpfaq/archive/2010/08/12/blocking-collection-and-the-producer-consumer-problem.aspx

Here's the example of back-grounding without a background worker

MainWindow.xaml - it's just a window with a status label. The min necessary to get the point across:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Width="378" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="94" SizeToContent="Height">
    <Grid>
        <StackPanel>
            <Button Click="Button_Click">Press Me</Button>
            <StatusBar>
                <Label Name="TheLabel" Content="Status" />
            </StatusBar>
        </StackPanel>
    </Grid>
</Window>

Here's the Code Behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading.Tasks;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        // Dispatchers are an easy way to update the UI from a background thread
        private System.Windows.Threading.Dispatcher Dispatcher;

        // the Current Task - think of this as your background worker
        private Task CurrentTask;


        public MainWindow()
        {
            InitializeComponent();

            // this line needs to run on the main thread.

            Dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
        }

        private void UpdateStatus(string msg)
        {
            // marshall the call back to the main thread
            Dispatcher.Invoke( new Action( () => TheLabel.Content = msg ));
        }

        // we're going to calculate the average of a sh*tload (technical term) of numbers
        private void DoSomeWork()
        {
            // update the UI
            UpdateStatus("Doing Work");

            Random r = new Random();
            int max = r.Next(100000000, 1000000000);
            double avg = Enumerable.Range(0, max).Average();

            // update the UI
            UpdateStatus(string.Format("Done - Average = {0}", avg));
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // make sure the current task isn't already running
            if (CurrentTask != null && CurrentTask.IsCompleted == false)
            {
                MessageBox.Show("Wait");
                return;
            }

            // start a new one
            CurrentTask = Task.Factory.StartNew(() =>
                {
                    DoSomeWork();
                });
        }
    }
}

Upvotes: 1

Wayne Brantley
Wayne Brantley

Reputation: 694

You can easily do multithreading using Threadpools. However, the trouble and issues you will have everything to do with your code that produces this XML file. If you restrict this code to only be running one thread at a time - but your UI still responsive, you will have less issues with your code. (Instead of trying to run a thread and process multiple web pages at one time)

Upvotes: 0

Related Questions