5

Here's the scenario: In my WPF app I'd like to keep a loop running at all times that does various things. This pattern came to mind:

    void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
    {
        SomeProcessAsync(); //throw away task
    }

    async Task SomeProcessAsync()
    {
        while (true)
        {
            DoSomething();

            await Task.Delay(1000);
        }
    }

The call triggers a warning since the return value is unused. What is the cleanest way to silence that warning?

#pragma warning disable 4014
            AddItemsAsync(); //throw away task
#pragma warning restore 4014

This works but it looks so nasty!

Btw, I also could have used a timer but I liked the simplicity of this loop.

boot4life
  • 4,966
  • 7
  • 25
  • 47
  • 1
    how about using `void` as return type ? – Selman Genç Mar 16 '17 at 15:33
  • 1
    Why do you have two methods in the first place? Just have the loop right in `MainWindow_OnLoaded`. – Servy Mar 16 '17 at 15:35
  • 1
    Why don't you use a timer instead of this loop? `Task.Delay` itself uses a timer underneath. This isn't a fire-and-forget task, it's something that should be active as long as the form or application is active – Panagiotis Kanavos Mar 16 '17 at 15:38
  • I'd like to avoid async void if I don't require it's particular behavior. Also, the method SomeProcessAsync should not need to know what code is calling it. It should expose the Task and it's the callers job to discard it. – boot4life Mar 16 '17 at 15:44
  • @Servy good point but assume an already big Load handler and the question becomes relevant again. – boot4life Mar 16 '17 at 15:45
  • @boot4life what is the loop doing? You could use a timer, a Dataflow ActionBlock or Rx to perform periodic work or have a queue for processing jobs in the background. You'll need a way to terminate the loop gracefully when the form closes or the application terminates – Panagiotis Kanavos Mar 16 '17 at 16:02

5 Answers5

25

As already mentioned in chris' answer, the right solution here is to turn the event handler into an async void method and then use await, so that exceptions are propagated correctly.

But if you really want to ignore the Task, then you can assign it to a variable:

var ignored = SomeProcessAsync();

Or in C# 7.0, you can use discard:

_ = SomeProcessAsync();
svick
  • 236,525
  • 50
  • 385
  • 514
  • 5
    Interestingly, I get no compiler warnings here. The compiler team must have thought of this case. – boot4life Mar 16 '17 at 18:20
  • @boot4life I believe that's what it's meant to be. Visual Studio right now even suggests `use discard '_'`, if you press on "show potential fixes" – Dennis Meissel Sep 09 '22 at 10:19
8

You can make the event handler async:

async void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
    await SomeProcessAsync(); //throw away task
}

Normally, async void is bad, but it's necessary when an event handler is async and exceptions should be handled here instead of in whatever calls this. You can (and should) use the normal ConfigureAwait(false) if SomeProcessAsync doesn't need the UI context.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Reposting a previous comment since it applies fully: I'd like to avoid async void if I don't require it's particular behavior. Also, the method SomeProcessAsync should not need to know what code is calling it. It should expose the Task and it's the callers job to discard it. Maybe in this simple piece of code this technique is OK but I'd like to find a general solution. – boot4life Mar 16 '17 at 15:46
  • So the solution you're looking for is when you want a synchronous method to make use of an asynchronous method. The event handler isn't a great example of that because it can be asynchronous. It would be good to include that requirement in the original question. I'm not sure what specific behaviour of `async void` you're saving for when you want it - as far as I remember, it's the exceptions that are a problem, but exceptions you intend to catch escaping the event handler are also a problem. In addition, this code imposes no requirements on `SomeProcessAsync`. – chris Mar 16 '17 at 15:50
  • @boot4life Calling an async method and dropping the task on the floor is functionally identical to an `async void` method. The problem with an `async void `method is you're *forcing* the caller to drop the task on the floor, which is why it's problematic to use it when that's not actually the desired behavior. – Servy Mar 16 '17 at 15:52
1

My solution is to silence the compiler warning with a little helper method that is reusable:

static class TaskHelpers
{
    /// <summary>Signifies that the argument is intentionally ignored.</summary>
    public static void DiscardTask(this Task ignored)
    {
    }
}

And the call looks like this:

AddItemsAsync().DiscardTask();

That's clean and self-documenting. Still looking for a better name for the helper.

boot4life
  • 4,966
  • 7
  • 25
  • 47
  • Check [this](http://stackoverflow.com/q/22629951/1768303) and [this](http://stackoverflow.com/q/22864367/1768303). – noseratio Mar 17 '17 at 00:57
  • In Italy the `DiscardTask` extension method would be called `ForgetAboutIt`. – Fred May 17 '21 at 14:05
0

Async-await uses threads from the thread pool. Although the number of threads in the thread pool is fairly large, and possibly adjustable, it is still a limited number of threads.

The threads in the thread pool are optimized for short living tasks. They start and finish fast, the results from these thread can be accessed fairly easily. But these advantages come with a cost. If not, all threads would be threads from the thread pool.

If you want to let your thread do something for a fairly long time, it is best to use a regular System.Threading.Thread, possibly wrapped in a System.ComponentModel.BackgroundWorker.

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
-3

Better to create a task with your delegate code Task.Factory.StartNew( () => {} );

GCamel
  • 612
  • 4
  • 8
  • This won't be able to access UI controls. – boot4life Mar 16 '17 at 15:45
  • of course yes, use Dispatcher.BeginInvoke++++for UI call – GCamel Mar 16 '17 at 15:47
  • 2
    @GCamel Sounds like you haven't ever used the TPL before. When using the TPL you are almost certainly doing something wrong if you're calling `BeginInvoke`. Anyway, this doesn't solve the problem in any way; you're still discarding a `Task`, you're just creating additional work that accomplishes nothing productive (and actively harms lots of the code) in the process. – Servy Mar 16 '17 at 15:49
  • @GCamel no they aren't. They are a way to **await** an already executing task. As for picking on Servy ... well, you don't get a gold badge for `async/await` for commenting. ViewModels and *databinding* have nothing to do with the current question either. – Panagiotis Kanavos Mar 16 '17 at 15:58