I have 2 projects. One of them aspnet core webapi and second one is console application which is consuming api.
Api method looks like:
[HttpPost]
public async Task<IActionResult> CreateBillingInfo(BillingSummary
billingSummaryCreateDto)
{
var role = User.FindFirst(ClaimTypes.Role).Value;
if (role != "admin")
{
return BadRequest("Available only for admin");
}
... other properties
billingSummaryCreateDto.Price = icu * roc.Price;
billingSummaryCreateDto.Project =
await _context.Projects.FirstOrDefaultAsync(x => x.Id ==
billingSummaryCreateDto.ProjectId);
await _context.BillingSummaries.AddAsync(billingSummaryCreateDto);
await _context.SaveChangesAsync();
return StatusCode(201);
}
Console application which consuming api:
public static async Task CreateBillingSummary(int projectId)
{
var json = JsonConvert.SerializeObject(new {projectId});
var data = new StringContent(json, Encoding.UTF8, "application/json");
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", await Token.GetToken());
var loginResponse = await client.PostAsync(LibvirtUrls.createBillingSummaryUrl,
data);
WriteLine("Response Status Code: " + (int) loginResponse.StatusCode);
string result = loginResponse.Content.ReadAsStringAsync().Result;
WriteLine(result);
}
Program.cs main method looks like:
static async Task Main(string[] args)
{
if (Environment.GetEnvironmentVariable("TAIKUN_USER") == null ||
Environment.GetEnvironmentVariable("TAIKUN_PASSWORD") == null ||
Environment.GetEnvironmentVariable("TAIKUN_URL") == null)
{
Console.WriteLine("Please specify all credentials");
Environment.Exit(0);
}
Timer timer = new Timer(1000); // show time every second
timer.Elapsed += Timer_Elapsed;
timer.Start();
while (true)
{
Thread.Sleep(1000); // after 1 second begin
await PollerRequests.CreateBillingSummary(60); // auto id
await PollerRequests.CreateBillingSummary(59); // auto id
Thread.Sleep(3600000); // 1hour wait again requests
}
}
Is it possible find all id and paste it automatically instead of 59 and 60? Ids from projects table. _context.Projects
Tried also approach using method which returns ids
public static async Task<IEnumerable<int>> GetProjectIds2()
{
var json = await
Helpers.Transformer(LibvirtUrls.projectsUrl);
List<ProjectListDto> vmList =
JsonConvert.DeserializeObject<List<ProjectListDto>>(json);
return vmList.Select(x => x.Id).AsEnumerable(); // tried
ToList() as well
}
and in main method used:
foreach (var i in await PollerRequests.GetProjectIds2())
new List<int> { i }
.ForEach(async c => await
PollerRequests.CreateBillingSummary(c));
for first 3 ids it worked but does not get other ones,
tested with console writeline method returns all ids
First get all Ids:
var ids = await PollerRequests.GetProjectIds2();
Then create list of task and run all tasks:
var taskList = new List<Task>();
foreach(var id in ids)
taskList.Add(PollerRequests.CreateBillingSummary(id));
await Task.WhenAll(taskList);
Related
I want to improve the performance and remove the delay in showing the data to the user on the screen. As per requirement, I need to get the list of the data from a different source, then get the further data from other sources based on the previous data which takes a lot of time and feel that executing them sequentially.
I am looking for the suggestion to improve the performance, asynchronously call the client and wait at the end and reduce the wait time of the request.
foreach (var n in player.data)
{
var request1 = new HttpRequestMessage(HttpMethod.Get, "https://api.*****.com/buckets/" + **** + "/tests/" + n.id);
var client1 = new HttpClient();
request1.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "****-b23a-*****-b1be-********");
HttpResponseMessage response1 = await client1.SendAsync(request1, HttpCompletionOption.ResponseHeadersRead);
List<dataroot> root1 = new List<dataroot>();
if (response1.StatusCode == System.Net.HttpStatusCode.OK)
{
try
{
var apiString1 = await response1.Content.ReadAsStringAsync();
var player1 = Newtonsoft.Json.JsonConvert.DeserializeObject<envRoot>(apiString1);
if (!string.IsNullOrEmpty(player1.data.environments[0].parent_environment_id))
{
player.data.Where(x => x.id == player1.data.environments[0].test_id).ToList().ForEach(s => s.isShared = true);
player.data.Where(x => x.id == player1.data.environments[0].test_id).ToList().ForEach(s => s.sharedEnvironmentId = player1.data.environments[0].parent_environment_id);
//player.data.Where(x=>x.id==player1.data.environments[0].test_id).ToList().ForEach(s=>s.sharedEnvironmentId=player1.data.environments[0].test_id);
}
player.data.Where(x => x.id == player1.data.environments[0].test_id).ToList().ForEach(s => s.normalenvironmentId = player1.data.environments[0].id);
}
catch (Exception ex)
{
var test = ex;
}
}
}
You can try the way I did in my sample below:
https://github.com/rajabb/RunningLongRunningTasksEfficientlyAndWaitAtEnd
The main part of code is:
List<Task> tasks = new List<Task>();
for (int i = 0; i < 100; i++)
{
tasks.Add(LongRunningTask.RunAsync(i.ToString()));
}
await Task.WhenAll(tasks.ToArray());
I have a controller which is following;
public class ScheduleController: ControllerBase
{
private readonly IScheduleService _scheduleService;
public ScheduleController(IScheduleService scheduleService)
{
_scheduleService = scheduleService;
}
[HttpGet]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleResponseDto>> GetDraftSchedule(int plantCode)
{
var schedule = await _scheduleService.GetLatestScheduleForPlant(plantCode);
return Ok(schedule);
}
[HttpPost]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleResponseDto>> PostSchedule(int plantCode, List<ScheduleInputItemDto> scheduleInputItems)
{
var schedule = await _scheduleService.AddNewSchedule(plantCode, scheduleInputItems);
return Ok(schedule);
}
[HttpPost("items")]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleItemResponseDto>> PostScheduleItem(int scheduleId, ScheduleInputItemDto scheduleInputItem)
{
var scheduleItem = await _scheduleService.AddItemToSchedule(scheduleId, scheduleInputItem);
return Ok(scheduleItem);
}
[HttpPut("items/{itemId}")]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleItemResponseDto>> PutScheduleItem(int scheduleId, int itemId, ScheduleInputItemDto scheduleInputItem)
{
var scheduleItem = await _scheduleService.ChangeScheduleItem(scheduleId, itemId, scheduleInputItem);
return Ok(scheduleItem);
}
}
User adding new schedule and items to schedule. I need to check if items are overlapping by their start date and end date. I have been trying to find a solution to prevent items to be added if they are overlapping.
In AddItemToSchedule method I need to check if they are overlapping.
public async Task<ScheduleResponseDto> AddItemToSchedule(int scheduleId, ScheduleInputItemDto scheduleItem)
{
var scheduleWithId = await _scheduleRepository.GetScheduleById(scheduleId);
scheduleWithId.AddItem(
start: scheduleItem.Start,
end: scheduleItem.End,
cementType: scheduleItem.CementType,
now: DateTime.UtcNow);
await _scheduleRepository.Update(scheduleWithId);
return scheduleWithId.MapToScheduleDto();
}
And Also same thing for ChangeScheduleItem method.
public async Task<ScheduleResponseDto> ChangeScheduleItem(int scheduleId, int itemId, ScheduleInputItemDto scheduleInputItem)
{
var now = DateTime.UtcNow;
var schedule = await _scheduleRepository.GetScheduleById(scheduleId);
schedule.UpdateItem(itemId, scheduleInputItem.Start, scheduleInputItem.End, scheduleInputItem.CementType, now);
await _scheduleRepository.Update(schedule);
return schedule.MapToScheduleDto();
}
The first test is for adding new item to schedule and testing with Task.WhenAll() what if two different user call the endpoint with the same two items at the same time.
[Fact]
public async Task GivenScheduleWithNoItems_WhenTwoSimultaneousIdenticalAddItemRequests_ThenOneItemIsAddedAndTheOtherRejected()
{
//Setup
var fixture = new Fixture();
var plantCode = fixture.Create<int>().ToString();
var itemToAdd = new ScheduleInputItemDto
{
Start = DateTime.UtcNow,
End = DateTime.UtcNow.AddHours(1),
CementType = "CEM-I"
};
var addScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var latestScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var addItemForScheduleRequest = (string scheduleId) => NewRequest
.AddRoute("schedule/items")
.AddQueryParams("scheduleId", scheduleId);
// Exercise
await addScheduleRequest.Post(new ScheduleInputItemDto[]{});
// First let's get the schedule before adding any items. This schedule is currently empty..
var scheduleBeforeAddition = await latestScheduleRequest.Get<ScheduleResponseDto>();
var scheduleId = scheduleBeforeAddition.ScheduleId.ToString();
var addItemRequest = addItemForScheduleRequest(scheduleId);
// Simultaneously start two tasks that will make the same exact item addition request.
// This is a race condition, the first request should pass and the second should fail.
var itemAddResponses = await Task.WhenAll(addItemRequest.Post(itemToAdd, false), addItemRequest.Post(itemToAdd, false));
//Finally let's get the schedule after the item addition requests. It should have only one item in it.
var scheduleAfterAddition = await latestScheduleRequest.Get<ScheduleResponseDto>();
// Verify
scheduleBeforeAddition.ScheduleItems.Count.Should().Be(0);
//TEST FAILS HERE - only one of the items should be added and the second should cause a conflict
scheduleAfterAddition.ScheduleItems.Count.Should().Be(1);
var failures = itemAddResponses.ToList().Where(it => it.IsSuccessStatusCode == false);
var successes = itemAddResponses.ToList().Where(it => it.IsSuccessStatusCode == true);
failures.Count().Should().Be(1);
successes.Count().Should().Be(1);
}
The second test is for also checking the same thing during changing the item in the schedule.
[Fact]
public async Task GivenScheduleWithItem_WhenTwoClientsAreChangingTheSingleItem_ThenItemModificationShouldHappenInSequence()
{
//Setup
var fixture = new Fixture();
var plantCode = fixture.Create<int>().ToString();
var itemDto = new ScheduleInputItemDto
{
Start = DateTime.UtcNow,
End = DateTime.UtcNow.AddHours(1),
CementType = "CEM-I"
};
var addScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var latestScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var changeItemForScheduleRequest = (string scheduleId, string itemId) => NewRequest
.AddRoute($"schedule/items/{itemId}")
.AddQueryParams("scheduleId", scheduleId);
//Exercise
//Make new schedule
await addScheduleRequest.Post(new List<ScheduleInputItemDto> { itemDto });
var scheduleBeforeChanges = await latestScheduleRequest.Get<ScheduleResponseDto>();
var scheduleId = scheduleBeforeChanges.ScheduleId.ToString();
var existingItemId = scheduleBeforeChanges.ScheduleItems.First().ScheduleItemId;
var itemChangeRequest = changeItemForScheduleRequest(scheduleId, existingItemId.ToString());
// Send two simultaneous item change requests
var itemChangeResponses = await Task.WhenAll(itemChangeRequest.Put(itemDto, false), itemChangeRequest.Put(itemDto, false));
//Get the schedule after item change requests, should have only one item and the item should have an update counter of only 1
var scheduleAfterChanges = await latestScheduleRequest.Get<ScheduleResponseDto>();
//verify
scheduleBeforeChanges.ScheduleItems.Count.Should().Be(1);
scheduleBeforeChanges.ScheduleItems.First().NumberOfTimesUpdated.Should().Be(0);
scheduleAfterChanges.ScheduleItems.Count.Should().Be(1);
scheduleAfterChanges.ScheduleItems.First().NumberOfTimesUpdated.Should().Be(1);
var failures = itemChangeResponses.ToList().Where(it => it.IsSuccessStatusCode == false);
var successes = itemChangeResponses.ToList().Where(it => it.IsSuccessStatusCode == true);
//TEST FAILS HERE, as one of the calls should fail
failures.Count().Should().Be(1);
successes.Count().Should().Be(1);
}
In conclusion I need to prevent methods to add overlapping items in the schedule.
Example Json Input is the following;
{
"scheduleId": 12132891,
"plantId": 1213,
"updatedOn": "2021-12-01T12:44:17Z",
"scheduleItems": [
{
"scheduleItemId": 1,
"cementType": "CEM-I",
"start": "2021-11-23T00:00:00Z",
"end": "2021-11-23T02:15:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
},
{
"scheduleItemId": 2,
"cementType": "CEM-II",
"start": "2021-11-23T03:00:00Z",
"end": "2021-11-23T10:30:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
},
{
"scheduleItemId": 3,
"cementType": "CEM-III",
"start": "2021-11-23T10:30:00Z",
"end": "2021-11-23T11:00:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
}
]
}
I wrote a web crawler and I want to know if my approach is correct. The only issue I'm facing is that it stops after some hours of crawling. No exception, it just stops.
1 - the private members and the constructor:
private const int CONCURRENT_CONNECTIONS = 5;
private readonly HttpClient _client;
private readonly string[] _services = new string[2] {
"https://example.com/items?id=ID_HERE",
"https://another_example.com/items?id=ID_HERE"
}
private readonly List<SemaphoreSlim> _semaphores;
public Crawler() {
ServicePointManager.DefaultConnectionLimit = CONCURRENT_CONNECTIONS;
_client = new HttpClient();
_semaphores = new List<SemaphoreSlim>();
foreach (var _ in _services) {
_semaphores.Add(new SemaphoreSlim(CONCURRENT_CONNECTIONS));
}
}
Single HttpClient instance.
The _services is just a string array that contains the URL, they are not the same domain.
I'm using semaphores (one per domain) since I read that it's not a good idea to use the network queue (I don't remember how it calls).
2 - The Run method, which is the one I will call to start crawling.
public async Run(List<int> ids) {
const int BATCH_COUNT = 1000;
var svcIndex = 0;
var tasks = new List<Task<string>>(BATCH_COUNT);
foreach (var itemId in ids) {
tasks.Add(DownloadItem(svcIndex, _services[svcIndex].Replace("ID_HERE", $"{itemId}")));
if (++svcIndex >= _services.Length) {
svcIndex = 0;
}
if (tasks.Count >= BATCH_COUNT) {
var results = await Task.WhenAll(tasks);
await SaveDownloadedData(results);
tasks.Clear();
}
}
if (tasks.Count > 0) {
var results = await Task.WhenAll(tasks);
await SaveDownloadedData(results);
tasks.Clear();
}
}
DownloadItem is an async function that actually makes the GET request, note that I'm not awaiting it here.
If the number of tasks reaches the BATCH_COUNT, I will await all to complete and save the results to file.
3 - The DownloadItem function.
private async Task<string> DownloadItem(int serviceIndex, string link) {
var needReleaseSemaphore = true;
var result = string.Empty;
try {
await _semaphores[serviceIndex].WaitAsync();
var r = await _client.GetStringAsync(link);
_semaphores[serviceIndex].Release();
needReleaseSemaphore = false;
// DUE TO JSON SIZE, I NEED TO REMOVE A VALUE (IT'S USELESS FOR ME)
var obj = JObject.Parse(r);
if (obj.ContainsKey("blah")) {
obj.Remove("blah");
}
result = obj.ToString(Formatting.None);
} catch {
result = string.Empty;
// SINCE I GOT AN EXCEPTION, I WILL 'LOCK' THIS SERVICE FOR 1 MINUTE.
// IF I RELEASED THIS SEMAPHORE, I WILL LOCK IT AGAIN FIRST.
if (!needReleaseSemaphore) {
await _semaphores[serviceIndex].WaitAsync();
needReleaseSemaphore = true;
}
await Task.Delay(60_000);
} finally {
// RELEASE THE SEMAPHORE, IF NEEDED.
if (needReleaseSemaphore) {
_semaphores[serviceIndex].Release();
}
}
return result;
}
4- The function that saves the result.
private async Task SaveDownloadedData(List<string> myData) {
using var fs = new FileStream("./output.dat", FileMode.Append);
foreach (var res in myData) {
var blob = Encoding.UTF8.GetBytes(res);
await fs.WriteAsync(BitConverter.GetBytes((uint)blob.Length));
await fs.WriteAsync(blob);
}
await fs.DisposeAsync();
}
5- Finally, the Main function.
static async Task Main(string[] args) {
var crawler = new Crawler();
var items = LoadItemIds();
await crawler.Run(items);
}
After all this, is my approach correct? I need to make millions of requests, will take some weeks/months to gather all data I need (due to the connection limit).
After 12 - 14 hours, it just stops and I need to manually restart the app (memory usage is ok, my VPS has 1 GB and it never used more than 60%).
How to run an async Task without blocking other tasks?
I have one function that iterates though a List but the problem is that when the function is called other functions won't work again until the first function is done. What are the ways of making the HandleAsync function non-blocking ?
public static async Task HandleAsync(Message message, TelegramBotClient bot)
{
await Search(message, bot); // This should be handled without working other possible functions. I have a function similar to this but which doesn't iterate though any list.
}
private static async Task Search(Message message, TelegramBotClient bot)
{
var textSplit = message.Text.Split(new[] {' '}, 2);
if (textSplit.Length == 1)
{
await bot.SendTextMessageAsync(message.From.Id, "Failed to fetch sales. Missing game name. ",
ParseMode.Html);
}
else
{
var search = await Program.itad.SearchGameAsync(textSplit[1], limit: 10, cts: Program.Cts);
if (search.Data != null)
{
var builder = new StringBuilder();
foreach (var deal in search.Data.List)
{
var title = deal.Title;
var plain = deal.Plain;
var shop = deal.Shop != null ? deal.Shop.Name : "N/A";
var urls = deal.Urls;
var priceNew = deal.PriceNew;
var priceOld = deal.PriceOld;
var priceCut = deal.PriceCut;
builder.AppendLine($"<b>Title:</b> {title}");
builder.AppendLine($"<b>Shop:</b> {shop}");
builder.AppendLine();
builder.AppendLine($"<b>Price:</b> <strike>{priceOld}€</strike> | {priceNew}€ (-{priceCut}%)");
var buttons = new[]
{
new[]
{
InlineKeyboardButton.WithUrl("Buy", urls.Buy.AbsoluteUri),
InlineKeyboardButton.WithUrl("History",
urls.Game.AbsoluteUri.Replace("info", "history"))
}
};
var keyboard = new InlineKeyboardMarkup(buttons);
var info = await Program.itad.GetInfoAsync(plain, cts: Program.Cts);
var image = info.Data.GameInfo.Image;
if (image == null) image = new Uri("https://i.imgur.com/J7zLBLg.png");
await TelegramBot.Bot.SendPhotoAsync(message.From.Id, new InputOnlineFile(image.AbsoluteUri),
builder.ToString(), ParseMode.Html, replyMarkup: keyboard,
cancellationToken: Program.Cts.Token);
builder.Clear();
}
}
else
{
await bot.SendTextMessageAsync(message.From.Id, "Failed to fetch sales. Game not found. ",
ParseMode.Html);
}
}
I'm trying to send multiple same requests at (almost) once to my WebAPI to do some performance testing.
For this, I am calling PerformRequest multiple times and wait for them using await Task.WhenAll.
I want to calculate the time that each request takes to complete plus the start time of each one of them. In my code,however, I don't know what happens if the result of R3 (request number 3) comes before R1? Would the duration be wrong?
From what I see in the results, I think the results are mixing with each other. For example, the R4's result sets as R1's result. So any help would be appreciated.
GlobalStopWatcher is a static class that I'm using to find the start time of each request.
Basically I want to make sure that elapsedMilliseconds and Duration of each request is associated with the request itself.
So that if the result of request 10th comes before the result of 1st request, then duration would be duration = elapsedTime(10th)-(startTime(1st)). Isn't that the case?
I wanted to add a lock but it seems impossible to add it where there's await keyword.
public async Task<RequestResult> PerformRequest(RequestPayload requestPayload)
{
var url = "myUrl.com";
var client = new RestClient(url) { Timeout = -1 };
var request = new RestRequest { Method = Method.POST };
request.AddHeaders(requestPayload.Headers);
foreach (var cookie in requestPayload.Cookies)
{
request.AddCookie(cookie.Key, cookie.Value);
}
request.AddJsonBody(requestPayload.BodyRequest);
var st = new Stopwatch();
st.Start();
var elapsedMilliseconds = GlobalStopWatcher.Stopwatch.ElapsedMilliseconds;
var result = await client.ExecuteAsync(request).ConfigureAwait(false);
st.Stop();
var duration = st.ElapsedMilliseconds;
return new RequestResult()
{
Millisecond = elapsedMilliseconds,
Content = result.Content,
Duration = duration
};
}
public async Task RunAllTasks(int numberOfRequests)
{
GlobalStopWatcher.Stopwatch.Start();
var arrTasks = new Task<RequestResult>[numberOfRequests];
for (var i = 0; i < numberOfRequests; i++)
{
arrTasks[i] = _requestService.PerformRequest(requestPayload, false);
}
var results = await Task.WhenAll(arrTasks).ConfigureAwait(false);
RequestsFinished?.Invoke(this, results.ToList());
}
Where I think you're going wrong with this is trying to use a static GlobalStopWatcher and then pushing this code into your function that you're testing.
You should keep everything separate and use a new instance of Stopwatch for each RunAllTasks call.
Let's make it so.
Start with these:
public async Task<RequestResult<R>> ExecuteAsync<R>(Stopwatch global, Func<Task<R>> process)
{
var s = global.ElapsedMilliseconds;
var c = await process();
var d = global.ElapsedMilliseconds - s;
return new RequestResult<R>()
{
Content = c,
Millisecond = s,
Duration = d
};
}
public class RequestResult<R>
{
public R Content;
public long Millisecond;
public long Duration;
}
Now you're in a position to test anything that fits the signature of Func<Task<R>>.
Let's try this:
public async Task<int> DummyAsync(int x)
{
await Task.Delay(TimeSpan.FromSeconds(x % 3));
return x;
}
We can set up a test like this:
public async Task<RequestResult<int>[]> RunAllTasks(int numberOfRequests)
{
var sw = Stopwatch.StartNew();
var tasks =
from i in Enumerable.Range(0, numberOfRequests)
select ExecuteAsync<int>(sw, () => DummyAsync(i));
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
Note that the line var sw = Stopwatch.StartNew(); captures a new Stopwatch for each RunAllTasks call. Nothing is actually "global" anymore.
If I execute that with RunAllTasks(7) then I get this result:
It runs and it counts correctly.
Now you can just refactor your PerformRequest method to just do what it needs to:
public async Task<string> PerformRequest(RequestPayload requestPayload)
{
var url = "myUrl.com";
var client = new RestClient(url) { Timeout = -1 };
var request = new RestRequest { Method = Method.POST };
request.AddHeaders(requestPayload.Headers);
foreach (var cookie in requestPayload.Cookies)
{
request.AddCookie(cookie.Key, cookie.Value);
}
request.AddJsonBody(requestPayload.BodyRequest);
var response = await client.ExecuteAsync(request);
return response.Content;
}
Running the tests is easy:
public async Task<RequestResult<string>[]> RunAllTasks(int numberOfRequests)
{
var sw = Stopwatch.StartNew();
var tasks =
from i in Enumerable.Range(0, numberOfRequests)
select ExecuteAsync<string>(sw, () => _requestService.PerformRequest(requestPayload));
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
If there's any doubt about the thread-safety of Stopwatch then you could do this:
public async Task<RequestResult<R>> ExecuteAsync<R>(Func<long> getMilliseconds, Func<Task<R>> process)
{
var s = getMilliseconds();
var c = await process();
var d = getMilliseconds() - s;
return new RequestResult<R>()
{
Content = c,
Millisecond = s,
Duration = d
};
}
public async Task<RequestResult<int>[]> RunAllTasks(int numberOfRequests)
{
var sw = Stopwatch.StartNew();
var tasks =
from i in Enumerable.Range(0, numberOfRequests)
select ExecuteAsync<int>(() => { lock (sw) { return sw.ElapsedMilliseconds; } }, () => DummyAsync(i));
return await Task.WhenAll(tasks).ConfigureAwait(false);
}