I have a situation like this:
var retrievalTasks = new Task[2];
retrievalTasks[0] = GetNodesAsync();
retrievalTasks[1] = GetAssetsToHandleAsync();
Task.WaitAll(retrievalTasks);
And I would like the result of retrievalTasks[0] and retrievalTasks[1] to be stored in variables.
I could achieve this with:
var a = await GetNodesAsync();
var b = await GetAssetsToHandleAsync();;
But I would rather not await both like that, because then they're not fired at the same time right? Or am I misunderstanding?
I've seen this as a reference: Awaiting multiple Tasks with different results
But I think this is a slightly different scenario that wouldn't work in my case.
Any ideas?
Thanks
EDIT:
await Task.WhenAll(catTask, houseTask, carTask);
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
This just seemed to wait four times to get the three results. However, #armenm has shown how to avoid that.
Here we go, you need to have separate variables for the tasks having respective types:
var task1 = GetNodesAsync();
var task2 = GetAssetsToHandleAsync();
Task.WaitAll(new Task[] { task1, task2 });
var result1 = task1.Result;
var result2 = task2.Result;
But I would recommend to make it asynchronous:
var task1 = GetNodesAsync();
var task2 = GetAssetsToHandleAsync();
await Task.WhenAll(new Task[] { task1, task2 });
var result1 = task1.Result;
var result2 = task2.Result;
Related
I would like to write some game in a bot in which the text changes very VERY often. But I don't understand how to do it without using declared new var
Please help form a permanent button jump, but without hard code
[Command("test_button")]
public async Task But(CommandContext ctx)
{
var button = await ctx.RespondAsync(m => m.WithContent("blya")
.AddComponents(new DiscordButtonComponent(ButtonStyle.Primary, "button-one", "button")));
var people = new string[] { "textAAA", "textBBB", "textCCC" };
var result = await button.WaitForButtonAsync();
await result.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
await button.ModifyAsync(people[0]).ConfigureAwait(false);
// how to fix it?
var result2 = await button.WaitForButtonAsync();
await result2.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
await button.ModifyAsync(people[1]).ConfigureAwait(false);
var result3 = await button.WaitForButtonAsync();
await result3.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
await button.ModifyAsync(people[2]).ConfigureAwait(false);
I thought it could be done through delegates or => but I'm still new to programming, so I don't understand how to do it
I would be very grateful for advice or hints
I just needed a loop "for", I'm groggy
for (int i = 0; i < 3; i++)
{
var result = await one.WaitForButtonAsync();
await result.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
await one.ModifyAsync(text[i]).ConfigureAwait(false);
}
My media bag is getting populated inside of the foreach, but when it hits the bottom line the mediaBag is empty?
var mediaBag = new ConcurrentBag<MediaDto>();
Parallel.ForEach(mediaList,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
async media =>
{
var imgBytes = await this.blobStorageService.ReadMedia(media.BlobID, Enums.MediaType.Image);
var fileContent = Convert.ToBase64String(imgBytes);
var image = new MediaDto()
{
ImageId = media.MediaID,
Title = media.Title,
Description = media.Description,
ImageContent = fileContent
};
mediaBag.Add(image);
});
return mediaBag.ToList();
Is this because of my blobstorage function not being thread safe? what would this mean and what is the soultion if that is the case.
Parallel.ForEach doesn't work well with async actions.
You could start and store the tasks returned by ReadMedia in an array and then wait for them all to complete using Task.WhenAll before you create the MediaDto objects in parallel. Something like this:
var mediaBag = new ConcurrentBag<MediaDto>();
Task<byte[]>[] tasks = mediaList.Select(media => blobStorageService.ReadMedia(media.BlobID, Enums.MediaType.Image)).ToArray();
await Task.WhenAll(tasks);
Parallel.ForEach(imgBytes, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
bytes =>
{
var fileContent = Convert.ToBase64String(imgBytes);
var image = new MediaDto()
{
ImageId = media.MediaID,
Title = media.Title,
Description = media.Description,
ImageContent = fileContent
};
mediaBag.Add(image);
});
return mediaBag.ToList();
Parallelism isn't concurrency. Parallel.ForEach is meant for data parallelism, not executing concurrent actions. It partitions the input data and uses as many worker tasks as there are cores to process one partition each. It doesn't work at all with asynchronous methods because that would defeat its very purpose.
What you ask for is concurrent operations - eg downloading 100 files, 4 or 6 at a time. One way would be to just launch all 100 tasks and wait for them to finish. That's a bit extreme and will probably flood the network connection.
A better way to do this would be to use a TPL Dataflow block like TransformBlock with a specific DOP, eg :
var options = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4 };
var buffer=new BufferBlock<MediaDto>();
var block=new TransformBlock<ThatMedia,MediaDto>(media =>{
var imgBytes = await this.blobStorageService.ReadMedia(media.BlobID, Enums.MediaType.Image);
var fileContent = Convert.ToBase64String(imgBytes);
var image = new MediaDto()
{
ImageId = media.MediaID,
Title = media.Title,
Description = media.Description,
ImageContent = fileContent
};
return image;
},options);
block.LinkTo(buffer);
After that, you can start posting entries to the block.
foreach(var entry in mediaList)
{
block.Post(entry);
}
block.Complete();
await block.Completion;
if(buffer.TryReceiveAll(out var theNewList))
{
...
}
Thanks for the advice, i believe i may of misunderstood a 'Parallel.ForEach' usecase.
i have modified the function to use a list of tasks instead and it works very nicely. Below is the changes i made.
var mediaBag = new ConcurrentBag<MediaDto>();
IEnumerable<Task> mediaTasks = mediaList.Select(async m =>
{
var imgBytes = await this.blobStorageService.ReadMedia(m.BlobID, Enums.MediaType.Image);
var fileContent = Convert.ToBase64String(imgBytes);
var image = new MediaDto()
{
ImageId = m.MediaID,
Title = m.Title,
Description = m.Description,
ImageContent = fileContent
};
mediaBag.Add(image);
});
await Task.WhenAll(mediaTasks);
return mediaBag.ToList();
This is the C# Code
app.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubConfiguration);
CpuEngine cpuEngine = new CpuEngine(1500);
MemoryEngine memoryEngine = new MemoryEngine(1500);
// Task.Factory.StartNew(async () => await cpuEngine.StartCpuCheck());
// Task.Factory.StartNew(async () => await memoryEngine.StartCheckMemory());
Only the first one is running. How can I run each other?
1) Use Task.Run instead.
2) Remove the keywords async and await in the lambda.
3) Use Task.WhenAll and pass in the two tasks.
public async Task InvokeAsync()
{
var cpuEngine = new CpuEngine(1500);
var memoryEngine = new MemoryEngine(1500);
await Task.WhenAll(
Task.Run(() => cpuEngine.StartCpuCheck()),
Task.Run(() => memoryEngine.StartCheckMemory()));
}
Hello I'm wondering if there is a cleaner way of writing the async code below. Basically I want to wait on all the tasks, but one of the tasks are optional. It feels needlessly elaborate, thinking if I can do it through some callback but haven't been able to figure it out.
var mobile = true;
var task1 = _service.Async1();
var tasks = new List<Task>
{
task1
};
Task<int> task2 = null;
if (!mobile)
{
task2 = _service.Async2();
tasks.Add(task2);
}
await Task.WhenAll(tasks);
var result1 = task1.Result;
if (!mobile)
{
result2 = task2.Result;
// Do stuff
}
There is no need to create a list and await all the results at once. Why not await it when you need it? If task2 runs much longer than task1, you can at least start processing it, long before task2 is done.
Something like this:
var task1 = _service.Async1();
Task<int> task2 = null;
if (!mobile)
{
task2 = _service.Async2();
}
var result1 = await task1;
if (!mobile)
{
var result2 = await task2;
// Do stuff
}
I saw couple of posts on this... but most people are manually creating tasks rather than receiving it from an API.
I have this code
Task[] tasks = new Task[companyCount];
// start all threads
for(int i = 0; i < companyCount; i++) {
string input = GetNextCompanyRecord(i);
string token = GetUserToken();
HttpClient client = GetHttpClient();
StringContent content = CreateStringContent(input);
var x = client.PostAsync(companyUrlString, content).ConfigureAwait(continueOnCapturedContext: false);
tasks[i] = x;
}
Task.WaitAll(tasks);
the objective is that I start the task for each record in a for loop and then when all tasks are started ... doing a single waitall.
problem is that I cannot assign the x variable to the task array.
The PostAsync method is not returning me the Task. instead it is returning ConfiguredTaskAwaitable. But my hope was that I will get Task and I will be able to add that to my array and then do a waitall.
Just do it like this:
Task<HttpResponseMessage>[] tasks = new Task<HttpResponseMessage>[companyCount];
for(int i = 0; i < companyCount; i++)
{
string input = GetNextCompanyRecord(i);
string token = GetUserToken();
HttpClient client = GetHttpClient();
StringContent content = CreateStringContent(input);
var x = client.PostAsync(companyUrlString, content);
tasks[i] = x;
}
Task.WaitAll(tasks);
But, perhaps a cleaner way would be to do this:
var tasks =
from i in Enumerable.Range(0, companyCount)
let input = GetNextCompanyRecord(i)
let token = GetUserToken()
let client = GetHttpClient()
let content = CreateStringContent(input)
select client.PostAsync(companyUrlString, content);
Task.WaitAll(tasks.ToArray());
Or even better yet:
using (var client = GetHttpClient())
{
var tasks =
from i in Enumerable.Range(0, companyCount)
let input = GetNextCompanyRecord(i)
let token = GetUserToken()
let content = CreateStringContent(input)
select client.PostAsync(companyUrlString, content);
Task.WaitAll(tasks.ToArray());
}
PostAsync is returning a Task (a Task<HttpResponseMessage>, to be precise), but then you're calling ConfigureAwait on it, which returns a ConfiguredTaskAwaitable.
Try getting a reference to the task first, and then calling ConfigureAwait.
var task = client.PostAsync(companyUrlString, content);
ConfiguredTaskAwaitable awaitable = task.ConfigureAwait(continueOnCapturedContext: false);
tasks[i] = task;
Also, consider:
removing the call to ConfigureAwait, since you don't seem to need it anyway.
changing your array from Task[] to Task<HttpResponseMessage>[], if you want to retrieve the tasks' results when they're done.