简体   繁体   中英

Does threads work with an await statement?

This is in C#.

I have an async function called Login in which is a await statement, to wait for the login into the api. This was working fine. So i made it, that this async function got called from a other thread, because I have more than one login at a time. I first testet the thread without the await statement and it runs fine through the function. Now I tried a real login with the await statement and my thread just stops at the await statemant and does nothing anymore.

        public async void Login()
        {

            var loginRequest = await api.LoginAsync();

            if (loginRequest.Succeeded)
            {
                status = "LOGGED_IN";
                message = "logged in";
            } else
            {
                status = "ERROR";
                message = loginRequest.Info.Message;
            }
            Brain.BotRespond(ID);
        }

That you have several logins at the same time is not a reason to put this in a separate thread.

One thing that helped me a lot to understand async-await was this interview with Eric Lippert . Search somewhere in the middle for async await.

He compares async-await with a cook who has to make several breakfasts. The cook puts the kettle on to make some tea. He doesn't wait idly until the water is boiling. Instead he looks around to see if he can do something else instead: he can put the bread in the toaster, or start with the second breakfast.

Notice that the only moments that the cook will have to wait, is when another process, like the kettle or the bread toaster is doing something: if doesn't speed up the process if the cook waits idly or not.

You will see something similar in computers: you will find async await only when there is another autonomous process involved. Like when you want to write data to a disk, or query data from a database management system, or read information from the internet: your process can't do anything to speed up the process, while the other process is doing its stuff, your process can do something else, like listen to new operator input.

If the cook also has to do some heavy processing, like slicing tomatoes, he will be to busy to handle the warm toast. In that case, it might be wise to hire a 2nd cook to slice the tomatoes. Once the water boils, the bread is toasted, the cook has to wait for the 2nd cook to finish his slicing before he can continue finishing the breakfast.

Your problem is the same: you ask an operator or process to login. Instead of idly waiting for the login to complete, you can do other things, for instance start the next login, and while that one is waiting to complete, you can start a third login: all done by the same thread.

Only if you have to do some lengthy heavy processing, without waiting idly, it might be useful to let a separate thread do this heavy calculations. Only do this, to keep your callers responsive. If none of your callers will be doing anything useful while your are busy slicing tomatoes, don't hire an extra cook for this, because that will make you idle, and the result is that it takes longer to finish breakfast.

By the way: every Async method returns Task<TResult> instead of TResult and Task instead of void . There is only one exception to this rule: event handlers return async void , no one can wait for an event handler to finish.

So your api has a LoginAsync, and you want to create a function that will use this Login. Maybe you have some other Logins as well, and you want to call them all, without waiting idly (well, if no one finishes the login, you'll have nothing to do anymore)

  • async Task LoginAsync() // = the Login from your API

The Login method, let's make it a sync and async version, so you can see why it is wise to separate the login action from the processing of the login:

void Login()
{
    LoginResult loginResult = api.Login();
    this.ProcessLoginResult(loginResult);
}
async Task LoginAsync()
{
    LoginResult loginResult = await api.Login();
    this.ProcessLoginResult(loginResult);
    // as this is not a lengthy process, there is no async method
}

You were talking about several logins, so let's assume you have a procedure that starts several Logins. Apparently your login methods don't need to inform their callers about the result of the login, all you need to know is whether they are all finished:

async Task PerformMultipleLoginsAsync()
{
    // start Login 1, do not await yet
    Task taskLogin1 = this.LoginAsync();

    // because you did not await, you are free to do something else
    // while LoginAsync is awaiting
    this.DoSomething();

    // start a second Login. The first might be finished, but you don't care about
    // that right now
    Task taskLogin2 = this.LoginAsync();
    this.DoSomethingElse();

    // now you need the 1st login to finish
    await taskLogin1;

    // or if you need both logins to be finished
    await Task.WhenAll(new Task[] {taskLogin1, taskLogin2});

    // of if you need that at least one login is finished:
    await Task.WhenAny(new Task[] {taskLogin1, taskLogin2});
}

Waiting for any of the logins would be nice if you login on several processes and want to do something as soon as any of the processes finishes:

HashSet<Task<LoginResult>> loginTasks = new HashSet(this.StartManyLogins());

while (loginTasks.Any())
{
    Task<LoginResult> finishedTask = await Tasks.WhenAny(loginTasks);
    LoginResult loginResult = finishedTask.Result;
    this.ProcessLoginResult(loginResult);

    loginTasks.Remove(finishedTask);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM