2

I have an async method in c# code, as shown in demo code--

public async Task<string> Method1()
{
    string s = "some text";
    await method2();
    return s;
}

Now the problem is - method2 take few minutes to complete but the UI update depends only on string s returned by Method1. This is hitting UI performance. Is there a way to return string s and do not wait for await call to complete.

Henk Mollema
  • 44,194
  • 12
  • 93
  • 104
Hershika Sharma
  • 105
  • 1
  • 14
  • 7
    If you don't want to wait until method2() finishes, then this function design is wrong. What are you trying to achieve with this code. May be a bigger picture will help better solve the problem. – Prateek Shrivastava Sep 13 '19 at 06:48
  • If you dont want to wait then don't await. `await method2();` -> `method2();` – FCin Sep 13 '19 at 06:48
  • 5
    What should happen if `Method2` throws an exception? – Jodrell Sep 13 '19 at 06:50
  • 1
    If the returned string does not depend on calling `method2()` then split `Method1()` into two methods - one non-async one that returns the string, and an async one the awaits `method2()`, If the returned string *does* depend on `method2()` then you have no choice but to await it. – Matthew Watson Sep 13 '19 at 07:44
  • we can use ConfigureAwait(false) while calling method2() with await. As a good practice, ConfigureAwait(false) helps in increasing performance. https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – Hershika Sharma Oct 11 '19 at 06:28

4 Answers4

1

You could just write

public string Method1()
{
    Method2();
    return "some text";
}

However, you'll get warnings.

You are doing a fire and forget on Method2, indicating that you don't care if Method2 succeeds, fails, throws an exception or even completes before the host of your application shuts down.

I doubt very much this is what you actually want?

Jodrell
  • 34,946
  • 5
  • 87
  • 124
  • Also, if an unhandled exception happens in `Method2()` it can crash your whole app. – Gabriel Luci Sep 13 '19 at 15:51
  • @GabrielLuci No it cannot, unless it is a `StackOverflowException` which would crash an app anyway. Every exception will be discarded since there is no `Task` object returned by `Method1`. `IAsyncStateMachine` will simply store exceptions in a defaulted `TaskAwaiter` that will later be discarded. – FCin Sep 15 '19 at 04:38
  • I just tested this. You're right, if `Method2()` returns a `Task`. If it's `async void`, then it will crash the application. Since the original code was awaiting `Method2()`, then it would return a `Task` and it would be safe. – Gabriel Luci Sep 17 '19 at 12:31
0

You could use Task.Run() to call the async method without waiting for the reply.

 static void Main(string[] args)
        {
            var t = Task.Run(() => MyMethod());
        }

        static public async void MyMethod()
        {
            //some long running code
        }

This page details it more:

Simplest way to do a fire and forget method in c# 4.0

MDBarbier
  • 379
  • 3
  • 12
  • 1
    There is no point in calling `Task.Run` if the method already executes asynchronous methods... You are trying to wrap IO based execution in a method intended for CPU based execution. – FCin Sep 13 '19 at 07:50
  • Fair enough - I thought that just calling it without awaiting would generate a warning but it seems it does not in VS2019. – MDBarbier Sep 13 '19 at 07:57
  • @FCin: That's not true. It depends on what context this code is running in, which unfortunately we don't have visibility into. What you're saying is true for something like ASP.NET Core, but in a console app, WPF, WinForms, etc. the syncrhonization context will cause the async task to run on the UI thread by default. Using `Task.Run` ensures it goes to a different thread. – Chris Pratt Sep 13 '19 at 12:38
  • @ChrisPratt If e.g. WPF would schedule a method call on UI thread then it would block the UI thread, blocking windows message pump, which is not the usual case. Scheduler _might_ schedule a task on UI thread, but it doesn't have to. Depending on the implementation of `method2` it might schedule it on ThreadPool thread, create a timer/spinwait, create a windows pipe, return completed task, create a completely new thread, who knows what sits inside the method. Everything depends on whetever `method2` is asynchronously awaited internally creating AsyncTaskMethodBuilder – FCin Sep 13 '19 at 13:04
0

Apparently, your wrote in the specifications of Method1 that this function would do something that requires you to call Method2, otherwise you could write your procedure without calling Method2.

This means, that your caller expects some post condition. Alas you didn't write your post condition, so I have to write the following using fairly abstract terms.

Suppose Method2 has a post-condition Post2 (I'm leaving the pre-condition here out of the discussion)

Now what did you write as specification of Method1? I know you wrote a simplified version of your actual Method1, but it seems to be something like:

Method2 will return a Task<string>, that, when awaited for, will have changed the state of the process into: Post2 AND the return value will be a string containing "some text".

If this is your specification, then you definitely should await for Method2.

An alternative specification could be:

Method2 will return a Task<string>, that, when awaited for, will have started Method2 AND the return value will be a string containing "some text".

If this is the case, you don't have to await for Method2. Your caller has no way to find out when Post2 is active, nor whether a problem occurred when trying to change the state of the machine to Post2.

It might be that at some point one of your callers want to be certain that Method2 has completed, and thus Post2 is met. Why don't you let the caller decide whether he wants to await for Method2?

Instead of returning Task<string>, you could return a Tuple containing the Task for Method2 and the returning string. See Tuples in C# 7

public (string Text, Task Task> Method1()
{
    string s = "some text"; 
    Task method2Task = method2();

    return (Text: s, Task: method2Task);
}

Because you don't await, the method isn't async anymore.

Of course you can make do it in one statement:

return (Text: "Some text", Task: method2() );

Your caller should to something like:

var method1Result = method1();

// I'm not interested in Method2 right now, but I like to process the string result
ProcessText(method1Result.Text);

// if, after a while I want to be certain the Post2 is met, I'll have to await:
await method1Result.Task
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
-1

You can do that by removing await from method2 invocation and using Task.FromResult on the string object which you are returning but it would be little inefficient :

public async Task<string> Method1()
{
    string s = "some text";
    method2();
    return await Task.FromResult(s);
}
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
  • That will just run synchronously, but with the overhead of `await` having to decide to run synchronously. See Jodrell's answer. – Gabriel Luci Sep 13 '19 at 15:49
  • @GabrielLuci ofcourse that is the correct way, but what if we don't want to change the method signatures ? in that case this will be the way – Ehsan Sajjad Sep 13 '19 at 18:09