This is my code that works. I wouldn't have to do this if it weren't task based async, but using Monitor.Enter/Exit results in this problem Object synchronization method was called from an unsynchronized block of code. Exception on Mutex.Release()
People have mentioned using AutoResetEvent, and SemaphoreSlim, but I'm not quite sure which pattern fits.
private bool fakelock;
internal async Task<byte[][]> ExchangeCore(byte[][] apdus)
{
if (apdus == null || apdus.Length == 0)
return null;
List<byte[]> resultList = new List<byte[]>();
var lastAPDU = apdus.Last();
while (fakelock)
{
await Task.Delay(100);
}
fakelock = true;
foreach (var apdu in apdus)
{
await WriteAsync(apdu);
var result = await ReadAsync();
resultList.Add(result);
}
fakelock = false;
return resultList.ToArray();
}
You could maybe use a SemaphoreSlim which supports async.
private static SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1);
internal async Task<byte[][]> ExchangeCore(byte[][] apdus)
{
if (apdus == null || apdus.Length == 0)
return null;
await Semaphore.WaitAsync();
try
{
List<byte[]> resultList = new List<byte[]>();
foreach (var apdu in apdus)
{
await WriteAsync(apdu);
var result = await ReadAsync();
resultList.Add(result);
}
return resultList.ToArray();
}
finally
{
Semaphore.Release();
}
}
Related
I'd like to spawn some threads and in each thread sequentially make calls to an API and aggregate the results (some sort of stress testing). Here is my attempt:
private async Task DoWork()
{
var allResponses = new List<int>();
for (int i = 0; i < 10; i++)
{
await Task.Run(() =>
{
var responses = Enumerable.Range(0, 50).Select(i => CallApiAndGetStatusCode());
allResponses.AddRange(responses);
});
}
// do more work
}
private int CallApiAndGetStatusCode()
{
try
{
var request = new HttpRequestMessage(httpMethod.Get, "some url");
var responseResult = httpClient.SendAsync(request).Result;
return (int)responseResult.StatusCode;
}
catch (Exception e)
{
logger.LogError(e, "Calling API failed");
}
}
However, this code always ends up the catch with the inner exception being {"A task was canceled."}. What am I doing wrong here?
There is no benefit to using either Enumerable.Range or .AddRange in your example, since you do not need the seeded number. Your code must be converted to async/await to avoid deadlocks and in doing so, you can simply loop inside of each task and avoid any odd interactions between Enumerable.Select and await:
private async Task DoWork()
{
var allTasks = new List<Task>(10);
var allResponses = new List<int>();
for (int i = 0; i < 10; i++)
{
allTasks.Add(Task.Run(async () =>
{
var tempResults = new List<int>();
for (int i = 0; i < 50; i++)
{
var result = await CallApiAndGetStatusCode();
if (result > 0) tempResults.Add(result);
}
if (tempResults.Count > 0)
{
lock (allResponses)
{
allResponses.AddRange(tempResults);
}
}
}));
}
await Task.WhenAll(allTasks);
// do more work
}
private async Task<int> CallApiAndGetStatusCode()
{
try
{
var request = new HttpRequestMessage(HttpMethod.Get, "some url");
var responseResult = await httpClient.SendAsync(request);
return (int)responseResult.StatusCode;
}
catch (Exception e)
{
logger.LogError(e, "Calling API failed");
}
return -1;
}
Note that this code is overly protective, locking the overall batch before adding the temp results.
I changed your code to this and work
async Task DoWork()
{
var allResponses = new List<int>();
for (int i = 0; i < 10; i++)
{
await Task.Run(() =>
{
var responses = Enumerable.Range(0, 3).Select(i => CallApiAndGetStatusCodeAsync());
allResponses.AddRange(responses.Select(x => x.Result));
});
}
// do more work
}
async Task<int> CallApiAndGetStatusCodeAsync()
{
try
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://www.google.com");
var responseResult = await httpClient.SendAsync(request);
return (int)responseResult.StatusCode;
}
catch (Exception e)
{
logger.LogError(e, "Calling API failed");
return -1;
}
}
In the WPF .net core app there is the following:
An Observable Collection of items (itemObservCollection).
A static readonly HttpClient _httpclient
XML Responses
I am making a URL call to the api on each item in the observable collection (0 to 1000 items in collection). The return is XML. The XML is parsed using XElement. The property values in the observable collection are updated from the XML.
Task.Run is used to run the operation off the UI thread. Parallel.Foreach is used to make the calls in Parallel.
I feel I have made the solution overly complicated. Is there a way to simplify this? UpdateItems() is called from a button click.
private async Task UpdateItems()
{
try
{
await Task.Run(() => Parallel.ForEach(itemObservCollection, new ParallelOptions { MaxDegreeOfParallelism = 12 }, async item =>
{
try
{
var apiRequestString = $"http://localhost:6060/" + item.Name;
HttpResponseMessage httpResponseMessage = await _httpclient.GetAsync(apiRequestString);
var httpResponseStream = await httpResponseMessage.Content.ReadAsStreamAsync();
StringBuilder sb = new StringBuilder(1024);
XElement doc = XElement.Load(httpResponseStream);
foreach (var elem in doc.Descendants())
{
if (elem.Name == "ItemDetails")
{
var itemUpdate = itemObservCollection.FirstOrDefault(updateItem => updateItem.Name == item.Name);
if (itemUpdate != null)
{
itemUpdate.Price = decimal.Parse(elem.Attribute("Price").Value);
itemUpdate.Quantity = int.Parse(elem.Attribute("Quantity").Value);
}
}
}
}
catch (Exception ex)
{
LoggerTextBlock.Text = ('\n' + ex.ToString());
}
}));
}
catch (Exception ex)
{
LoggerTextBlock.Text = ('\n' + ex.ToString());
}
}
You could create an array of tasks and await them all using Task.WhenAll.
The following sample code kicks off a task per item in the ObservableCollection<int> and then wait asynchronously for all tasks to finish:
ObservableCollection<int> itemObservCollection =
new ObservableCollection<int>(Enumerable.Range(1, 10));
async Task SendAsync()
{
//query the HTTP API here...
await Task.Delay(1000);
}
await Task.WhenAll(itemObservCollection.Select(x => SendAsync()).ToArray());
If you want to limit the number of concurrent requests, you could either iterate through a subset of the source collecton to send requests in batches or use a SemaphoreSlim to limit the number of actual concurrent requests:
Task[] tasks = new Task[itemObservCollection.Count];
using (SemaphoreSlim semaphoreSlim = new SemaphoreSlim(12))
{
for (int i = 0; i < itemObservCollection.Count; ++i)
{
async Task SendAsync()
{
//query the HTTP API here...
try
{
await Task.Delay(5000);
}
finally
{
semaphoreSlim.Release();
}
}
await semaphoreSlim.WaitAsync();
tasks[i] = SendAsync();
}
await Task.WhenAll(tasks);
}
I would like to run several methods asyncron in a foreach. The return value should be written to a list.
The method is executed in a WPF application. The method GetItemPricesFromJsonAsync fetches from the web data.
public async Task LoadBlackMarketListView(List<MarketAnalysisManager.ItemTier> tiers, List<MarketAnalysisManager.ItemLevel> levels,
List<MarketAnalysisManager.ItemQuality> quialityList, string outdatedHours, string profit, Location? location)
{
await Task.Run(async () =>
{
var blackMarketSellObjectList = new List<BlackMarketSellObject>();
var items = await MarketAnalysisManager.GetItemListAsync(tiers, levels);
await Dispatcher.InvokeAsync(() =>
{
PbBlackMarketMode.Minimum = 0;
PbBlackMarketMode.Maximum = items.Count;
PbBlackMarketMode.Value = 0;
GridBlackMarketMode.IsEnabled = false;
LvBlackMarket.Visibility = Visibility.Hidden;
PbBlackMarketMode.Visibility = Visibility.Visible;
});
foreach (var item in items)
{
var allItemPrices = await MarketAnalysisManager.GetItemPricesFromJsonAsync(item.UniqueName, true);
if (allItemPrices.FindAll(a => a.City == Locations.GetName(Location.BlackMarket)).Count <= 0)
{
await IncreaseBlackMarketProgressBar();
continue;
}
blackMarketSellObjectList.AddRange(await GetBlackMarketSellObjectList(item, quialityList, allItemPrices, outdatedHours, profit, location));
await IncreaseBlackMarketProgressBar();
}
await Dispatcher.InvokeAsync(() =>
{
LvBlackMarket.ItemsSource = blackMarketSellObjectList;
PbBlackMarketMode.Visibility = Visibility.Hidden;
LvBlackMarket.Visibility = Visibility.Visible;
GridBlackMarketMode.IsEnabled = true;
});
});
}
Currently it looks like he's only doing one thing at a time.
Run... 0
End... 0
Run... 1
End... 1
Run... 2
End... 2
You will need to store the Tasks, not await them. Then you can wait for all of them.
Try this (replace your foreach with my code).
I would also advise you to use a real method instead of the annonymous one, it's much more readable.
List<Task> tasks = new List<Task>();
foreach (var item in items)
{
tasks.Add(Task.Run(async () =>
{
var allItemPrices = await MarketAnalysisManager.GetItemPricesFromJsonAsync(item.UniqueName, true);
if (allItemPrices.FindAll(a => a.City == Locations.GetName(Location.BlackMarket)).Count <= 0)
{
await IncreaseBlackMarketProgressBar();
return;
}
blackMarketSellObjectList.AddRange(await GetBlackMarketSellObjectList(item, quialityList, allItemPrices, outdatedHours, profit, location));
await IncreaseBlackMarketProgressBar();
}));
}
await Task.WhenAll(tasks);
Note: There is now a return instead of a continue since this is an annonymous function and you just have to end the function there instead of continuing with the foreach.
I require a Dataflow block which delays the forward of the message to the next block based on the timestamp in the message (LogEntry).
This is what i came up with but it feels not right. Any suggestions for improvements?
private IPropagatorBlock<LogEntry, LogEntry> DelayedForwardBlock()
{
var buffer = new ConcurrentQueue<LogEntry>();
var source = new BufferBlock<LogEntry>();
var target = new ActionBlock<LogEntry>(item =>
{
buffer.Enqueue(item);
});
Task.Run(() =>
{
LogEntry entry;
while (true)
{
entry = null;
if (buffer.TryPeek(out entry))
{
if (entry.UtcTimestamp < (DateTime.UtcNow - TimeSpan.FromMinutes(5)))
{
buffer.TryDequeue(out entry);
source.Post(entry);
}
}
}
});
target.Completion.ContinueWith(delegate
{
LogEntry entry;
while (buffer.TryDequeue(out entry))
{
source.Post(entry);
}
source.Complete();
});
return DataflowBlock.Encapsulate(target, source);
}
You could simply use a single TransformBlock that asynchronously waits out the delay using Task.Delay:
IPropagatorBlock<TItem, TItem> DelayedForwardBlock<TItem>(TimeSpan delay)
{
return new TransformBlock<TItem, TItem>(async item =>
{
await Task.Delay(delay);
return item;
});
}
Usage:
var block = DelayedForwardBlock<LogEntry>(TimeSpan.FromMinutes(5));
I'm trying to create an asynchronous console app that does a some work on a collection. I have one version which uses parallel for loop another version that uses async/await. I expected the async/await version to work similar to parallel version but it executes synchronously. What am I doing wrong?
public class Program
{
public static void Main(string[] args)
{
var worker = new Worker();
worker.ParallelInit();
var t = worker.Init();
t.Wait();
Console.ReadKey();
}
}
public class Worker
{
public async Task<bool> Init()
{
var series = Enumerable.Range(1, 5).ToList();
foreach(var i in series)
{
Console.WriteLine("Starting Process {0}", i);
var result = await DoWorkAsync(i);
if (result)
{
Console.WriteLine("Ending Process {0}", i);
}
}
return true;
}
public async Task<bool> DoWorkAsync(int i)
{
Console.WriteLine("working..{0}", i);
await Task.Delay(1000);
return true;
}
public bool ParallelInit()
{
var series = Enumerable.Range(1, 5).ToList();
Parallel.ForEach(series, i =>
{
Console.WriteLine("Starting Process {0}", i);
DoWorkAsync(i);
Console.WriteLine("Ending Process {0}", i);
});
return true;
}
}
The way you're using the await keyword tells C# that you want to wait each time you pass through the loop, which isn't parallel. You can rewrite your method like this to do what you want, by storing a list of Tasks and then awaiting them all with Task.WhenAll.
public async Task<bool> Init()
{
var series = Enumerable.Range(1, 5).ToList();
var tasks = new List<Task<Tuple<int, bool>>>();
foreach (var i in series)
{
Console.WriteLine("Starting Process {0}", i);
tasks.Add(DoWorkAsync(i));
}
foreach (var task in await Task.WhenAll(tasks))
{
if (task.Item2)
{
Console.WriteLine("Ending Process {0}", task.Item1);
}
}
return true;
}
public async Task<Tuple<int, bool>> DoWorkAsync(int i)
{
Console.WriteLine("working..{0}", i);
await Task.Delay(1000);
return Tuple.Create(i, true);
}
Your code waits for each operation (using await) to finish before starting the next iteration.
Therefore, you don't get any parallelism.
If you want to run an existing asynchronous operation in parallel, you don't need await; you just need to get a collection of Tasks and call Task.WhenAll() to return a task that waits for all of them:
return Task.WhenAll(list.Select(DoWorkAsync));
public async Task<bool> Init()
{
var series = Enumerable.Range(1, 5);
Task.WhenAll(series.Select(i => DoWorkAsync(i)));
return true;
}
In C# 7.0 you can use semantic names to each of the members of the tuple, here is Tim S.'s answer using the new syntax:
public async Task<bool> Init()
{
var series = Enumerable.Range(1, 5).ToList();
var tasks = new List<Task<(int Index, bool IsDone)>>();
foreach (var i in series)
{
Console.WriteLine("Starting Process {0}", i);
tasks.Add(DoWorkAsync(i));
}
foreach (var task in await Task.WhenAll(tasks))
{
if (task.IsDone)
{
Console.WriteLine("Ending Process {0}", task.Index);
}
}
return true;
}
public async Task<(int Index, bool IsDone)> DoWorkAsync(int i)
{
Console.WriteLine("working..{0}", i);
await Task.Delay(1000);
return (i, true);
}
You could also get rid of task. inside foreach:
// ...
foreach (var (IsDone, Index) in await Task.WhenAll(tasks))
{
if (IsDone)
{
Console.WriteLine("Ending Process {0}", Index);
}
}
// ...
We can use async method in foreach loop to run async API calls.
public static void Main(string[] args)
{
List<ZoneDetails> lst = GetRecords();
foreach (var item in lst)
{
//For loop run asyn
var result = GetAPIData(item.ZoneId, item.fitnessclassid).Result;
if (result != null && result.EventHistoryId != null)
{
UpdateDB(result);
}
}
}
private static async Task<FODBrandChannelLicense> GetAPIData(int zoneId, int fitnessclassid)
{
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = HttpClient.GetAsync(new Uri(url)).Result;
var content = response.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<Model>(content);
if (response.EnsureSuccessStatusCode().IsSuccessStatusCode)
{
Console.WriteLine($"API Call completed successfully");
}
return result;
}
To add to the already good answers here, it's always helpful to me to remember that the async method returns a Task.
So in the example in this question, each iteration of the loop has await. This causes the Init() method to return control to its caller with a Task<bool> - not a bool.
Thinking of await as just a magic word that causes execution state to be saved, then skipped to the next available line until ready, encourages confusion: "why doesn't the for loop just skip the line with await and go to the next statement?"
If instead you think of await as something more like a yield statement, that brings a Task with it when it returns control to the caller, in my opinion flow starts to make more sense: "the for loop stops at await, and returns control and the Task to the caller. The for loop won't continue until that is done."