1

thanks for reading this topic.

For a new WPF application (build in C#) I have a question regarding the design. The past few days I have read a lot about Async programming in C# (based on .NET 4.5).

What we would like to do is: Create a new async thread, which does independent background tasks. When this thread has data available: then send this data to the main program (by an public interface). So, the thread will set data in the main program and immediately return to the thread again. The main program will raise an event (INotifyPropertyChanged) when data has been changed.

What will be the best way to create this Async thread? Or at least, what would be the best way to design this feature?

At the moment I have build an application which creates a thread. This does not work Async at the moment:

    public MainWindow()
    {
        InitializeComponent();

        InitGuiInterface(this);

        //Create thread
        new OuterLabel_Thread(this);
    }

And the class "OuterLabel_Thread.cs"here below:

public class OuterLabel_Thread
{
    private MainWindow context = null;
    private bool exit = false;
    private int count = 0;

    public OuterLabel_Thread(MainWindow context)
    {
        this.context = context;

        Console.WriteLine("Running sample thread");
        Thread thread = new Thread(delegate ()
        {
            Console.WriteLine("Sample thread started");

            //start new task
            //run();
            Task.Factory.StartNew(run);
        });
        thread.Start();
    }

    public void Exit()
    {
        exit = true;
    }

    private void run()
    {
        while (!exit)
        {
            DateTime Time1 = DateTime.Now;

            if (context != null && context.GuiInterface != null)
            {
                //context.GuiInterface.UpdateThreadCount(count, "label_code_content");
            }
            Console.WriteLine("Background thread count = " + count);

            count++;
            if (count > 1000)
            {
                exit = true;
            }
            //Console.WriteLine((DateTime.Now - Time1).TotalMilliseconds.ToString());
            Thread.Sleep(10);
        }
    }
}

Many thanks in advance! Kind regards,

Rein.

4
  • Why don't you just use async await? that would make it easy to manage the UI thread and avoid cross thread errors. And you could avoid conccurency issues rather then trying to manage your own threads. Commented Oct 16, 2017 at 8:15
  • I do not have much experience with the use of Async and await. I know what these instructions do. Is your suggestion still based on the usage of the thread? How do I need to call the thread in that case? Commented Oct 16, 2017 at 8:31
  • Are you looking for making a Service that continously feeds data or just a long running task you start by say clicking a button? Commented Oct 16, 2017 at 8:54
  • I am looking for a service which recieve continiously data (processing data after third system has triggered a I/O device). This I/O device returns data by Ethernet. The service where we are talking about, will receive this data and set it in de main thread of our application. Commented Oct 16, 2017 at 13:03

3 Answers 3

3

as you want to keep the thread alive and as far as I understand, you don't know exactly when or if you will reach the 1000 mark, async might be the wrong choice. Correct me if i'm wrong.

For your case I would recommend using the BackgroundWorker:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
   int count = 0;
   BackgroundWorker worker = sender as BackgroundWorker;
        while (!exit)
        {
            DateTime Time1 = DateTime.Now;
            worker.ReportProgress(count);
            count++;
            if (count > 1000)
            {
                exit = true;
            }
            Thread.Sleep(10);
        }
}

// This event handler updates the progress.
        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            resultLabel.Text = ("Background thread count = " + e.ProgressPercentage.ToString());
        }

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled == true)
        {
            resultLabel.Text = "Canceled!";
        }
        else if (e.Error != null)
        {
            resultLabel.Text = "Error: " + e.Error.Message;
        }
        else
        {
            resultLabel.Text = "Done!";
        }
    }
Sign up to request clarification or add additional context in comments.

4 Comments

Agreed. BackgroundWorker is pretty powerful, yet underestimated class. Also running operations on many BackgroundWorker instances is equal running them on separate threads.
Thanks for your comment. In this case it is not necessary to create a thread, do I understand this correct?
BGW creates a thread itself. Here is some more info for you msdn.microsoft.com/en-us/library/…
Thanks for the feedback.
2

The best way would be using async+await and tasks.

    private async void LaunchButton_OnClick(object sender, RoutedEventArgs e)
    {
        resultLabel.Content = "Task running";
        resultLabel.Content = await SomeLongRunningTaskAsync();
    }

    private Task<string> SomeLongRunningTaskAsync()
    {
        return Task.Run(
            () =>
            {
                // Put your background work in here. with Task.Run it's not going to run on UI 
                int count = 0;
                while (count < 1000)
                {
                    count++;
                    Thread.Sleep(10);
                }

                return "Task done";
            });
    }

4 Comments

Thanks for your comment. I have copied this code into my application. When I am putting a breakpoint at one of the lines of the task, e.g.: at "Thread.Sleep(10);" Then my main program will also freeze. And the other way around as well. It that correct?
If a debugger breakpoint hits the application will always freeze. An easy way to detect if your UI thread isn't blocked is resizing the window and moving it around. Another way is having a button which can be clicked. If the UI is blocked you won't be able to click it properly
Although I'm a big fan of async, this new feature is not a "one size fit all" solution. If you need to update the status of the WPF controls in during the "run", even using ViewModels you can end up updating controls from the wrong thread or adding up ugly Dispatcher.BeginInvoke... code. As long you can guarantee that the method(s) you call won't break the "Run gui on gui thread", async is cool and the preferable way to do it. The worker solution in above case will make sure it won't ever happen...
@Marco Your considerations are absolutely correct. I'm just making sure the async task await answer is inside this question too, so people don't use very old API's to solve problems which can be solved in a rather simple way
1

I can't figure out if you are looking for a service or a long running task.

Since the others have good examples of long running tasks I've made a Service

It uses some advanced concpets like SynchronizationContext that you should read up on before using this in production code. Google async await and Stephen Cleary.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var foo = new FooService();
        foo.StartService(); // UI thrad calling
    }
}

public class FooService
{
    private SynchronizationContext _context;
    private CancellationTokenSource _cts;
    private CancellationToken _token;
    private Task _task;

    public void StartService()
    {
        _context = SynchronizationContext.Current; // Depends on the UI thread being the one to start the service or this will fail
        _cts = new CancellationTokenSource(10000); // Run for 10 seconds
        _token = _cts.Token;
        _task = Task.Run(() => Run(), _token);
    }

    public async Task Stop()
    {
        _cts.Cancel();
        await _task; // wait for task to finish
    }

    private void Run()
    {
        while (!_token.IsCancellationRequested)
        {
            // Do work                
            Thread.Sleep(1000);
            // Alternative use Control.Invoke() if you have access to a UI element, to delegate to the UI thread
            _context.Send((id) => Console.WriteLine($"Delegate from thread {id} to thread {Thread.CurrentThread.ManagedThreadId}"), Thread.CurrentThread.ManagedThreadId);
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.