1

Assuming the constructive criticism in this thread Thread-safe events - is this a "clean" way? I sat down and tried to read me in the whole thematics of multithreading.

Here's some code with the same base question: Is this an established way to create a non-freezing UI using events?

public partial class Form1 : Form
{
    public delegate void MyThreadUpdateHandler(string s);

    public event MyThreadUpdateHandler MyThreadUpdate;
    System.Threading.Thread MyThread;


    public Form1()
    {
        InitializeComponent();
    }

    void DoTheCount()
    {
        int a = 0;

        while (a < int.MaxValue)
        {
            if (a % 1000000 == 0)
            {
                this.MyThreadUpdate(a.ToString());
            }

            a++;
        }
    }

    private void Form1_MyThreadUpdate(string s)
    {
        this.lblEvent.Invoke((MethodInvoker) delegate ()
        {
            lblEvent.Text = s;
        });
    }

    private void btnStartThread_Click(object sender, EventArgs e)
    {
        MyThreadUpdate += Form1_MyThreadUpdate;

        if (MyThread == null)
            MyThread = new System.Threading.Thread(new System.Threading.ThreadStart(DoTheCount));

        lblStatus.Text = "Starting thread";


        if (MyThread.ThreadState == System.Threading.ThreadState.Unstarted)
            MyThread.Start();

    }

    private void btnAbortThread_Click(object sender, EventArgs e)
    {
        MyThread.Abort();
    }
}

However there's something I still don't get. Why are some examples using a Program() like this one? http://www.codeproject.com/Articles/667298/Using-ThreadStaticAttribute

Thank you :)

Community
  • 1
  • 1
AllDayPiano
  • 414
  • 1
  • 4
  • 20
  • The article you linked is about a CONSOLE app, not a windowed app, so it is quite different. – Matthew Watson Dec 04 '15 at 09:51
  • Ah well that clears things a little bit. Thank you. – AllDayPiano Dec 04 '15 at 09:52
  • What are you trying to get? your question is confusing... – Pavel Durov Dec 04 '15 at 09:52
  • I want to know if this is an established way to create a non-freezing UI using events. – AllDayPiano Dec 04 '15 at 09:54
  • Threads clean themselves up when they have no more code to execute. Please don't abort threads. – James Dec 04 '15 at 09:56
  • This was just for testing for me. I've tried some Thread methods. – AllDayPiano Dec 04 '15 at 10:13
  • 1
    I'd use Task instead of Thread directly Tasks are more handy and at higher level than Thread, and i suggest to use them with await/async – Not Important Dec 04 '15 at 10:15
  • [BackgroundWorker](https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx) is the established way of doing it. – Kosala W Dec 04 '15 at 10:15
  • @KosalaW Established does not mean good. The best way to do this is to do it single threaded. – Aron Dec 04 '15 at 10:16
  • @Aron: Did you mean to ask OP to do it in the UI thread? Is that the answer OP is looking for? – Kosala W Dec 04 '15 at 10:37
  • @KosalaW In this case, yes, it would be that events should be handled on the UI thread (for a UI application, single theaded, means on the UI thread). This example is far too artificial to be interesting, as it is unnecessarily CPU bound. The problem that the OP is facing is that his program is slow if he doesn't use "threading" but it will cause exceptions if he uses non-UI thread. The simplest solution (in .net 4.5+) is to use "Green Thread like" (ie async await) constructs/co-threads, unless he is CPU bound. – Aron Dec 04 '15 at 10:46
  • @Aron As far as I can understand, he is looking for a way to update a UI component during a long running task. – Kosala W Dec 04 '15 at 10:53
  • You're both right. I have to do a lot of hardware interaction i.e. com-port access. It may lead to a com port timeout which is at least 2 secounds. I don't like my UI to be blocked until the data is beeing read. But before I'm starting to rewrite the lib from my initial post (sie link above) I'd like to have a good way to head to before changing a whole bunch of code and seeing that it was a dead end :) – AllDayPiano Dec 04 '15 at 10:57
  • @KosalaW Ah...I didn't read his previous post. He actually fits into one of 3 situations where I would call threading "allowed". External library call that takes a long time. – Aron Dec 04 '15 at 10:58
  • @AllDayPiano Okay, I've posted an answer based on how to do it with Rx.Net (or MS.net's new event model). – Aron Dec 04 '15 at 11:10

2 Answers2

3

However there's something I still don't get. Why are some examples using a Program() like this one? http://www.codeproject.com/Articles/667298/Using-ThreadStaticAttribute

the code is

    static void Main(string[] args)
    {
        Program prog = new Program();

        //define the threads
        Thread thread1 = new Thread(new ThreadStart(prog.ThreadFunc1));
        Thread thread2 = new Thread(new ThreadStart(prog.ThreadFunc2));

This allowed the developer to call instance funcitons public void ThreadFunc1() & public void ThreadFunc2() from static funciton - static void Main(string[] args)

Update 1

Established way to create a non-freezing UI, is to do all the time consuming activity on a separate thread & marshal the call to UI thread only to update to it. What you have implemented follows the same idea.

Just one point I would like to mention, although it has nothing to do with freezing of UI.

instead of System.Threading.Thread, Please use ThreadPool.

Task, BackgroundWorker, System.Threading.Timer, Asynchronous Programming Model all use a thread from ThreadPool. or you may also use ThreadPool.QueueUserWorkItem

There are very special cases when one should use System.Threading.Thread instead of a thread from ThreadPool

Kapoor
  • 1,388
  • 11
  • 21
2

Firstly, let me say, I feel sorry for you, trying to work with a legacy library.

Okay. You qualify for one of the few reasons I would work with threading in .net

Running a Legacy library for a long time

Now, reading your previous code, I feel that you want to use Rx.Net for project.

I would start by creating a IObservable for your library.

public static class LibraryHelper
{
    public static IObservable<EventPattern<StatusChangedEventArg>> StatusObservable(this ComLibrary com)
    {
        return Observable.FromEventPattern<EventHandler<StatusChangedEventArg>, StatusChangedEventArg>(x => com.FooEvent +=x, x => com.FooEvent -= x);
    }
}

This shall allow you to use Rx.Net with your library, like so...

private async void btnStartThread_Click(object sender, EventArgs e)
{
    ComLibary com = new ComLibray();

    lblStatus.Text = "Starting thread";

    // using is there to unsubscribe after the call, to prevent a memory leak.
    var subscription = com.StatusObservable()
       //Magic sauce 
       //http://stackoverflow.com/questions/7417978/how-to-get-a-winform-synchronization-context-or-schedule-on-a-winform-thread
      .ObserveOn(SynchronizationContext.Current)
      .Subscribe(@event => {
         //Do something on the UI thread
      });
   using(subscription)
   {
           //Task.Run uses a background thread to get the data from your COM

          var result = await Task.Run(()=> com.Read());
           //back in UI thread. Do stuff.
    }
}
Aron
  • 15,464
  • 3
  • 31
  • 64