1

I have a method that runs some tasks in the background called backgroundworker5_doWork(). This method call the Invoke instruction which helps me to access the controls on my windows form (since a control cannot be accessed from another thread normally) as such

 private void backgroundWorker5_DoWork(object sender, DoWorkEventArgs e)
    {
        if(!isAdmin)
        {             
            //Start of invoke instruction   
            Invoke(new Action(() =>
            {
                if(test)
                {
                    return;
                }

                if (ValidateAll())
                {
                    comboBox5.Items.Clear();
                    packageToSendList.Clear();
                    progressBar3.Visible = true;
                    Cursor.Current = Cursors.WaitCursor;
                }

            }));
            //End of invoke

            RunAfterInvokeMethod() //Instructions called after the invoke 
       }
   }

My issue is that when true, the return instruction exits the invoke block (rightfully so) and executes the RunAfterInvokeMethod() which is not what I want. What I want is to exit the backgroundWorker5_doWork() method after the return is called.

I understand that it only exits the invoke instruction but is it possible to make it exit the parent method as well ? Maybe using other instructions ?

Thanks !

  • 1
    What about surrounding `RunAfterInvokeMethod()` with `if(!test)` or some other flag you make? – string.Empty Jul 26 '22 at 07:56
  • I also don't see anyone mentioning `goto` statements. (usually bad practice but if it avoids 2 flags I would say its usable here.) – string.Empty Jul 26 '22 at 08:11
  • 1
    Just a sidenote: Is `Invoke` marshalling to the UI Thread? In that case: You might want to think about what part of this needs to run in the background, what _can_ run in the background and what actually does. And I'd recommend to look into @JonasH 's [answer](https://stackoverflow.com/a/73119783/982149) which is basically suggesting to replace this with async/Task. – Fildor Jul 26 '22 at 08:31

3 Answers3

5

You can return a value (in this case, bool) from an invoked delegate:

bool result = (bool) Invoke(new Func<bool>(() =>
{
    if(test)
    {
        return false;
    }

    if (ValidateAll())
    {
        comboBox5.Items.Clear();
        packageToSendList.Clear();
        progressBar3.Visible = true;
        Cursor.Current = Cursors.WaitCursor;
    }

   return true;
}));
//End of invoke

if (result)
{
    RunAfterInvokeMethod() //Instructions called after the invoke 
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
4

You can create a bool to help you with that, something like

private void backgroundWorker5_DoWork(object sender, DoWorkEventArgs e)
{
    if(!isAdmin)
    {             
        //Start of invoke instruction   
        bool doThis = true;
        Invoke(new Action(() =>
        {
            if(test)
            {
                doThis = false;
                return;
            }

            if (ValidateAll())
            {
                comboBox5.Items.Clear();
                packageToSendList.Clear();
                progressBar3.Visible = true;
                Cursor.Current = Cursors.WaitCursor;
            }

        }));
        //End of invoke
        if(doThis)
            RunAfterInvokeMethod(); //Instructions called after the invoke 
   }
 }
Charlieface
  • 52,284
  • 6
  • 19
  • 43
aca
  • 1,071
  • 5
  • 15
  • `test` is already bool why don't you use that? – Alp Jul 26 '22 at 07:59
  • 1
    @Alp That's an option as well, but I don't see where it's declared, and I don't like using things like that. – aca Jul 26 '22 at 08:02
  • 2
    @alp It might be argued that `test` is a placeholder for something else, and it is also possible that it can change value. Using a separate variable will ensure it has the correct value. – JonasH Jul 26 '22 at 08:03
  • Thank you ! I am going to mark this as correct since it is the easiest solution and it does the job ! – Shorty Beast Jul 26 '22 at 08:51
  • @ShortyBeast Thanks, even thought occasionally the easiest one is not the best one, including this case – aca Jul 26 '22 at 08:54
  • @aca Yeah I agree with that Haha – Shorty Beast Jul 26 '22 at 08:56
3

The answer by aca would be the simplest solution.

I would however argue for rewriting your method to use async/await, since this makes it much easier to run code on a background thread while still updating the UI on the UI thread.

var result = await Task.Run(MyBackgroundWork);
UpdateMyUI(result); // continue on the UI thread once MyBackgroundWork has completed

If you want to do some more background work after updating the UI you can always do another Task.Run after updating the UI to schedule some more work to be run.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • Thanks for your answer ! Can I run multiple background tasks simultaneously using async/await? – Shorty Beast Jul 26 '22 at 08:55
  • 2
    @Shorty Beast Just run multiple `Task.Run`. It really depend on what you want to do. `Task.Run` for task parallelism, `Parallel.For` for data parallelism, Timers for reoccurring background tasks. DataFlow for pipelines etc. – JonasH Jul 26 '22 at 08:59
  • Oh wow didn't know about this ! I will take a further look into it. Thanks for your help ! – Shorty Beast Jul 26 '22 at 11:03