简体   繁体   中英

Tasks wait for each other to complete

I'm creating a Selenium bot as a WPF application. I'm using Tasks to create bots and then tasks to perform things on bot.

        foreach (string account in accounts)
        {
            count++;

            _ = Task.Run(async () =>
            {
                Bot bot = new Bot();

               //here is long code for what should bot do ASYNC

               //example
                await bot.Connect();                   
                await bot.SendTokens(); 
                await bot.SayHi(); 
            });
            await Task.Delay(600);
        }

I have array of accounts and I loop through them.

Let's say accounts.Length = 10 => I want to create 10 tasks that will 'have their own life' and run async inside of them. My problem is that Tasks wait for each other. Loop will create 10 tasks gradually (because of Task.Delay(600) ) => Bot will be created and start bot.Connect(); Lets say it takes always same amount of time to complete Connect() .

Logically first Task should be completed 600ms faster than second Task. Problem is that program waits until all Connect() are completed. Then it starts gradually all SendTokens() then complete them, but first task won't start SayHi() until SendTokens() of last bot in list is completed.

Why is that happening, I don't understand that. I tried setting

ThreadPool.SetMaxThreads(500, 500);

but that does nothing for me.

Here is example of live debug output, starts LoadOptions() gradually many times, but first LoadOptions() should be already completed and next method should be already started but it is waiting for all LoadOptions to end. I don't use in code Task.WaitAll() .

调试输出

Can anyone help me fix this issue and tell me what was wrong? I want to learn and know in future. Thank you very much and have a nice day.

As I understand it, your loop launches N bots and starts them on a "Life of their own" . The loop provides a spacing of 600 ms between Bot launches. The Bot class contains various async tasks, and for any given Bot there is an example start sequence of Connect , SendTokens , and SayHi where each of these three methods must complete before the next one starts.

But apart from that, each bot should be taking care of its own internal tasks concurrently while other bots are taking care of theirs. A typical concurrent timeline might look like this:

并发时间线

Here's one approach you could try to achieve this outcome. If the Bot class comes from a library where it can't be modified you can still make an async Extension method for it instead. (I'll show it that way just in case. Otherwise just make it a normal method of the Bot class.) Note that the return type is void ; the Bot has a life of its own per your post! We don't wait for it to live its life. HOWEVER it has internal business where one async activity must run to completion before the next one starts, and this extension method accounts for that.

static class Extensions
{
    public static async void PostInitAsyncRequest(this BlackBoxBot bot)
    {
        await bot.Connect();
        await bot.SendTokens();
        await bot.SayHi();
    }
}

All your WPF application needs to do is call the launchAllBots method which waits 600 ms between launches.

async void launchAllBots() 
{
    foreach (string account in new string[] 
    {"Jessie", "Ross", "Kensho", "Ernest", "Amy", "Andrew", "Baxter", "Jibo", "Buddy"})
    { 
        // Has its own life so DO NOT AWAIT
        var bot = new BlackBoxBot();
        bot.Account = account;
        bot.PostInitAsyncRequest();

        // But DO await the delay to space out Bot launches
        await Task.Delay(600);          
    }
}

SIMULATION In order to mock this startup timeline, the following fake Bot class was used:

// A crude mock of the 'Bot' class with internal async methods.
class BlackBoxBot
{
    public async Task Connect() 
    {
        Console.WriteLine($"{DateTime.Now.ToString("ss:fff")} {Account}: {nameof(Connect)}");
        await Task.Delay(_rando.Next(400, 800));
    }
    public async Task SendTokens()
    {
        Console.WriteLine($"{DateTime.Now.ToString("ss:fff")} {Account}: {nameof(SendTokens)}");
        await Task.Delay(_rando.Next(400, 800));
    }
    public async Task SayHi()
    {
        Console.WriteLine($"{DateTime.Now.ToString("ss:fff")} {Account}: {nameof(SayHi)}");
        await Task.Delay(_rando.Next(400, 800));
    }

    static Random _rando = new Random();

    public string Account { get; internal set; }
}

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