0

I have a class that gathers information about a machine (this is an example - in total GetInfo() may take minutes to run):

public class Scanner
{
    public void GetInfo()
    {
        this.Name = GetName();
        this.OS = GetOS();
    }

    public string Name { get; set; }
    public string OS { get; set; }
    public string Status { get; set; }

    private string GetName() { this.Status = "Getting computer name"; /*More operations*/ }
    private string GetOS() { this.Status = "Getting OS"; /*More operations*/ }
}

This is called by a form that needs to provide status feedback to the user.

TextBox1.Text = scanner.Status;

My question is, to achieve this, what is the best way to implement threading so that the application remains responsive?

I have got a BackgroundWorker running in the form and calling GetName(), GetOS() etc itself and that works fine, but the code isn't very repeatable - to keep maintenance low I want to keep a GetInfo() method in the class so if I need to run a scan from elsewhere theres only one piece of code that knows about how to.

I could move the BackgroundWorker in to GetInfo(), but then how would the form be able to check the Status property without doing a loop and locking up the UI?

Maybe have a BackgroundWorker in the form run GetInfo() and then run another Worker that would check Status and update the form if a change is detected?

This is my first proper go at Threading and can't get my head around what, I think, is a fairly common task so any help would be appreciated.

Note: I'm also well open to suggestions for other ways to implement the Status property - hopefully you get what I'm trying to achieve.

/edit: Some clarification.

Scanner.GetInfo() would be called manually, for example on a form button click. GetInfo() would then start populating the objects properties as it goes gathering information, and might take 5 minutes to complete.

I need to be able to keep the user up to date on its status, and the only way I can think of that happening (with my current knowledge) is for GetInfo() to update a 'Scanner.Status' property, which the form can then check at interval/within a loop and update the UI when it changes.

danabnormal
  • 462
  • 2
  • 8
  • 4
    Nowhere. Use `async/await` and Task.Run instead. It's trivial to write a loop with an `await Task.Delay(); var scanner =await Task.Run(MyPollingFunction); textBox1.Text=scanner.Status;` in its body, or create a timer with an `async` callback. – Panagiotis Kanavos Nov 14 '17 at 10:37
  • 1
    what triggers the call of `GetInfo` ? or when do you want it to happen? cyclic at certain intervals? or do you trigger it manually? – Mong Zhu Nov 14 '17 at 10:42
  • Is this used in a WPF/MVVM context? – Fildor Nov 14 '17 at 10:43
  • Have a look at Stephen C's blog: [Task.Run vs BackgroundWorker](https://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-intro.html) – Fildor Nov 14 '17 at 10:44
  • if you want to trigger it cyclic, may be just a `Timer` would solve your problem – Mong Zhu Nov 14 '17 at 11:31
  • Apologies, here is some clarification. GetInfo() would be called manually, for example on a button click. GetInfo() would then start populating the objects properties as it goes, and might take 5 minutes to complete. I need to be able to keep the user up to date on its status, and the only way I can think of that happening (with my current knowledge) is for GetInfo() to update a 'Status' property, which the form can then check at interval/within a loop and update the UI when it changes. I hope that clarifies. – danabnormal Nov 14 '17 at 11:39
  • @PanagiotisKanavos Thanks, I'll take a look. The two answers that have been submit also recommend this yet have been downvoted, am I assuming this is because of there content as apposed to correctness? – danabnormal Nov 14 '17 at 11:46
  • Liquid Core's answer was link-only. What was wrong with mine I don't know. The DV did not leave a message... Maybe there was a mistake I overlooked ... but that does not mean the overall suggestion is bad. – Fildor Nov 14 '17 at 12:15
  • BTW: _"which the form can then check at interval/within a loop and update the UI when it changes"_ - "Tell, don't ask". If you are in WPF/MVVM you should probably raise a NotifyPropertyChanged event for the GUI to update on changed Status instead of polling. – Fildor Nov 14 '17 at 12:19

1 Answers1

1

How about using INotifyPropertyChanged with a thread in the class as follows:

public class Scanner : INotifyPropertyChanged
{
  private string _Name, _OS;
  public event PropertyChangedEventHandler PropertyChanged;

  public string Name
  {
    get
    {
      return _Name;
    }
    set
    {
      if (value != _Name)
      {
        _Name = value;
        NotifyPropertyChanged("Name");
      }
    }
  }

  public string OS
  {
    get
    {
      return _OS;
    }
    set
    {
      if (value != _OS)
      {
        _OS = value;
        NotifyPropertyChanged("OS");
      }
    }
  }

  public void GetInfo()
  {
    _Name = string.Empty;
    _OS = string.Empty;

    new Thread(() => this.Name = GetName()).Start();
    new Thread(() => this.OS = GetOS()).Start();
  }

  private void NotifyPropertyChanged(string pName)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(pName));
    }
  }

  private string GetName() 
  { 
    return "long name operation here"; 
  }

  private string GetOS()
  {
    return "long OS operation here";
  }
}

Then in your form you could use the following:

Scanner m_Scanner = new Scanner();

public void Main()
{
  m_Scanner.PropertyChanged += UpdateGUI;
  m_Scanner.GetInfo();
}

private void UpdateGUI(object sender, PropertyChangedEventArgs e)
{
  if (e.PropertyName == "OS")
    txtOs.Text = m_Scanner.OS;
  else if (e.PropertyName == "Name")
    txtName.Text = m_Scanner.Name;
}
RooiWillie
  • 2,198
  • 1
  • 30
  • 36
  • This is pretty much where I was going given the responses above. I'm unable to access txtName (in your example) as its complaining its a cross-thread operation, but I'll see what I can come up with to resolve. – danabnormal Nov 14 '17 at 15:05
  • Have a look here about the cross-thread exception: https://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the – RooiWillie Nov 14 '17 at 16:19