Search code examples
c#linqasync-awaittasktask-parallel-library

Why isn't Task.WhenAll waiting until completion?


I'm running into an issue here and you might know the answer: I just couldn't figure it out still.

I'm creating a seed for a simple scenario here: adding likes to a given post. The method signature is:

async Task GeneratePostLike(string postId, string userId, CancellationToken cancellationToken = default) {
    return await postLikeService.AddUserLikeToPost(
      new PostLikeInput { PostId = postId, UserId = userId },
      cancellationToken
    );
}

I want to randomly generate likes to a post. I'm using random and other mechanisms to get a random number for doing such.

When I use an old for-loop block, it executes the way I want. This is the instructions I'm using:

var numberOfLikesForPost = random.Next(1, 100);
var randomUsersForLikes = await userService.GetRandomUsers(numberOfLikesForPost);

for (var likeIndex = 0; likeIndex < numberOfLikesForPost; likeIndex++)
{
  var randomUserId = randomUsersForLikes.PickRandom().Id;
  await GeneratePostLike(post.Id!, randomUserId, cancellationToken);
}

enter image description here

However, when I try to use Task.WhenAll, it simply executes one or two iterations and carries on. Seems like it's behaving like fire-and-forget of some sort. The following code represents what I'm doing with Task.WhenAll block:

var numberOfLikesForPost = random.Next(1, 100);
var randomUsersForLikes = await userService.GetRandomUsers(numberOfLikesForPost);

var generatePostLikeTasks = Enumerable.Repeat(
  GeneratePostLike(
    post.Id!,
    randomUsersForLikes.PickRandom().Id,
    cancellationToken
  ),
  numberOfLikesForPost
);

await Task.WhenAll(generatePostLikeTasks);

enter image description here

Do you have any suggestions of what can I add in order to make my Task.WhenAll block properly await execution of all tasks before carrying on?


Solution

  • var generatePostLikeTasks = Enumerable.Repeat(
      GeneratePostLike(
        post.Id!,
        randomUsersForLikes.PickRandom().Id,
        cancellationToken
      ),
      numberOfLikesForPost
    );
    

    This will not create numberOfLikesForPost number of Tasks but will just repeat the same Task instance for numberOfLikesForPost times.

    You should be able to change this to something like below. Haven't tested it yet because I don't have access to an IDE at the moment.

    var generatePostLikeTasks = Enumerable.Repeat(
      () => GeneratePostLike(
        post.Id!,
        randomUsersForLikes.PickRandom().Id,
        cancellationToken
      ),
      numberOfLikesForPost
    ).Select(f => f());