C# AWS Lambda and Thread.Sleep() - c#

I have a foreach loop running inside an AWS lambda, with a Thread.Sleep(2000) to throttle execution. However, the code doesn't seem to pause: I've logged timestamps before and after its execution. Here's a simplified version of the method:
private async Task<string> ExecuteAutoRetryRequest(string url)
{
string html = String.Empty;
int numberOfRetry = 3;
while (numberOfRetry > 0)
{
try
{
html = await ExecuteGetRequest(url);
if (!string.IsNullOrEmpty(html) && !html.Contains("Access Denied"))
{
break;
}
}
catch (Exception ex)
{
await LogException(ex.Message);
}
// Wait two seconds and try again
Console.WriteLine(DateTime.Now.ToString());
Thread.Sleep(2000);
Console.WriteLine(DateTime.Now.ToString());
numberOfRetry--;
}
return html;
}
I call it like so var htmlSnapShotResult = await ExecuteAutoRetryRequest(url);
I'm sure this is something basic about how lambdas operate that I'm unaware of, but any help would be appreciated.

Alexander's comment above is the correct answer: the following executes the pause correctly:
await Task.Delay(2000);

Related

Unity C# - Execute async task within a Coroutine and wait for it to finish

I'm completely new to Unity development and I'm trying to integrate an asynchronous functionality into an existing Coroutine but I have faced several issues so far.
My issues:
The Unity app completely freezes, probably because it was blocked by a thread. I implemented this code in traditional C# (console app) without having any issues.(Fixed now in Unity after some modifications)
The download task begins but it never finishes . This happens only when I run it as APK. On Unity debugging on PC works fine.
My code:
public void Validate()
{
StartCoroutine(DoWork());
}
private IEnumerator DoWork()
{
bool success;
//Perform some calculations here
..
..
success = true;
//
yield return new WaitForSeconds(3);
if(success){
GetInfo(_files);
}
}
async void GetInfo(List<string> files)
{
await StartDownload(files);
//After completing the download operation, perform some other actions in the background
...
...
//
//When done, change the active status of specific game objects
}
public async Task StartDownload(List<string> files){
var t1 = GetFileSizesAsync(files);
var t2 = DownloadFilesAsync(files);
await Task.WhenAll(t1, t2);
}
public async Task GetFileSizesAsync(List<string> urls)
{
foreach (var url in urls)
GetFileSize(url);
txtSize.text = totalSizeMb +"MB";
}
private void GetFileSize(string url)
{
var uri = new Uri(url);
var webRequest = HttpWebRequest.Create(uri);
webRequest.Method = "GET";
try
{
var webResponse = webRequest.GetResponse();
var fileSize = webResponse.Headers.Get("Content-Length");
var fileSizeInMegaByte = Math.Round(Convert.ToDouble(fileSize) / 1024.0 / 1024.0, 2);
totalSizeMb = totalSizeMb + fileSizeInMegaByte;
}
catch (Exception ex)
{
}
finally
{
webRequest.Abort();
}
}
public async Task<List<string>> DownloadFilesAsync(List<string> urls)
{
var result = new List<string>();
foreach (var url in urls)
var download = await DownloadFile(url);
if(download)
result.Add(url);
return response;
}
private async Task<bool> DownloadFile(string url)
{
WebClient webClient = new WebClient();
var uri = new Uri(url);
var saveHere = "C:\\...";
try
{
await webClient.DownloadFileTaskAsync(uri, saveHere);
return true;
}
catch (Exception ex)
{
return false;
}
}
Can you tell me what I'm doing wrong here? I've tried several ways but couldn't manage to find a proper solution.
Thank you!
I would rather make it
async Task GetInfo(List<string> files){ ... }
And then in your Coroutine do
var t = Task.Run(async () => await GetInfo(files));
while(!t.IsCompleted)
{
yield return null;
}
// Or also
//yield return new WaitUntil(() => t.IsCompleted);
if(!t.IsCompletedSuccesfully)
{
Debug.LogError("Task failed or canceled!");
yield break;
}
Note however:
When done, change the active status of specific game objects
This can't be done async! It has to happen in the Unity main thread! Therefore you would probably rather return something from your GetInfo task and activate the objects in the Coroutine when done.
after the loop and yielding you could then access the return value via
var result = t.Result;
Your web requests are currently totally redundant! You start get requests only to check how big the received content is but immediately throw away that received content ... then you start a second request to actually download the files (again).
In general I would recommend to rather use a UnityWebRequest.Get you can directly yield in the Coroutine in combination with a DownloadHandlerFile which allows you to directly download the content into a local file instead of into runtime memory.
Also
var saveHere = "C:\\...";
is hopefully not what you are trying to use as path on Android ;)
First of all.
The freezing is definitely being done by the IEneumerator as I don't see a yield statement. What's a yield statement? Uhhhh...Google it. lel
Second of all.
Wouldn't a normal void work just fine?
Third of all.
I don't know much about async's since I'm pretty new to them
but I'm fairly certain you don't need them for:
Task GetFileSizesAsync(List urls)
AND
async Task<List> DownloadFilesAsync(List urls)
I may be wrong tho.

Check IsCompleted from a void method with no return possible

I'm trying to get my head around async and await i have been trying to implement it in some code of mine, what I have done so far:
Code:
public async Task ExcecuteMacroCode(string _macroModeToUse, string[] _macroCode, string _site, Project _project)
{
try {
string macroSelected = _macroModeToUse == "-[REG]" ? "-[REG]" : "-[LAP]";
if (_macroModeToUse == macroSelected)
{
foreach (string _macroCodeFile in _macroCode)
{
string[] code = _macroCodeFile.Split('|');
switch (code[0])
{
case "RJ_U":
string cleanUrl = code[1].Replace("{HOSTNAME}", _site);
browser.Load(cleanUrl);
panelBrowserMain.Controls.Add(browser);
browser.Dock = DockStyle.Fill;
browserMain.Text = cleanUrl;
browser.AddressChanged += Browser_AddressChanged;
break;
case "RJ_I":
//await Task.Run(() => {
// return browser.ExecuteScriptAsyncWhenPageLoaded(GetAndReplaceMacro(code[1], _project));
//});
browser.ExecuteScriptAsyncWhenPageLoaded(GetAndReplaceMacro(code[1], _project));
break;
}
}
}
}
catch (Exception ex)
{
Helpers.DebugLogging($"[{DateTime.Now}]-[{ex}]");
}
}
public async Task InitializeChromeBrowserAsync(ChromiumWebBrowser browser, string _macroModeToUse, string[] _macroCode, string _site, int _sitesCount, Project _project, Func<string, Tuple<string, string, string>> getUserPassEmail)
{
try
{
Task newTask = await ExcecuteMacroCode(_macroModeToUse, _macroCode, _site, _project);
if (newTask.IsCompleted)
{
this.Close();
}
}
catch (Exception ex)
{
Helpers.DebugLogging($"[{DateTime.Now}]-[{ex}]");
}
}
The way i have set it up just now i'm getting the error Cannot implicitly convert type 'void' to 'System.Threading.Tasks.Task' from my understanding it's because the method await ExcecuteMacroCode is not returning anything (which is right, i don't need anything returned really)
Is there a way i can still make sure the Task is completed so i can use the newTask.IsCompleted part, i'm not even sure i have set the code up correctly any help would be appreciated.
You don't need to manually check if an awaited Task is completed or not. That's already done by the await operator.
The error is that this call await _wellboreService.CreateWellboreSectionAsync(wellboreSection, CurrentUser) is returning void, and you are trying to assign it to Task newTask.
When you await a task, the method is suspended and control is given to the parent caller until the Task is completed, and then it automatically resumes. After the await statement you know that the Task has, in fact, been completed because if not you wouldn't have reached that line in the execution. So there is no need for manually checking the IsCompleted property of the Task.
You could rewrite your code like this:
try
{
await ExcecuteMacroCode(_macroModeToUse, _macroCode, _site, _project);
this.Close();
}
catch (Exception ex)
{
Helpers.DebugLogging($"[{DateTime.Now}]-[{ex}]");
}
You should check the official documentation. for more clarifiation and examples such as this one:
public async Task<int> GetUrlContentLengthAsync()
{
var client = new HttpClient();
Task<string> getStringTask =
client.GetStringAsync("https://learn.microsoft.com/dotnet");
DoIndependentWork();
string contents = await getStringTask;
return contents.Length;
}
void DoIndependentWork()
{
Console.WriteLine("Working...");
}
Pay close attention to the await operator. It suspends GetUrlContentLengthAsync:
GetUrlContentLengthAsync can't continue until getStringTask
is complete.
Meanwhile, control returns to the caller of
GetUrlContentLengthAsync.
Control resumes here when
getStringTask is complete.
The await operator then retrieves
the string result from getStringTask.

Task unable to timeout

I have a implemented a simple task using TPL. It waits for 10 seconds to execute and returns true/false.
var checkCFOPTask = Task.Run(() => CheckCFOPExists());
checkCFOPTask.Wait(TimeSpan.FromSeconds(10));
if (checkCFOPTask.Result)
{
}
else
{
}
The problem is my code is stuck within the if statement.
if (checkCFOPTask.Result)
Each time I pause the debugger it still keeps waiting on the above line of code. This happened for the first time. Ideally it should return true/false within 10 seconds.
Below are the function definitions-
CheckCFOExists: Executed by the task.
private bool CheckCFOPExists()
{
bool found = false;
try
{
while (!found)
{
try
{
if (ieDriver.FindElement(By.Id("popup_message")).Text == "Não existem itens para realizar o rateio.")
{
ResetInvoiceSearchScreen();
break;
}
}
catch (Exception ex)
{
}
try
{
if (arrCFOPList.Contains(ieDriver.FindElement(By.Id("vendorNF.cfopOperCode")).GetAttribute("value")))
{
found = true;
}
}
catch (Exception ex)
{
}
}
}
catch (Exception ex)
{
}
return found;
}
ResetInvoiceSearchScreen: Executed within the above function
private void ResetInvoiceSearchScreen()
{
try
{
ieDriver.FindElement(By.Id("popup_ok")).Click();
ieDriver.FindElement(By.Id("ltmCnpjCpf")).Clear();
ieDriver.FindElement(By.Id("notaFiscalNbr")).Clear();
ieDriver.FindElement(By.Id("inbNotaFiscalId")).Clear();
ieDriver.FindElement(By.Id("seriesFrmCd")).Clear();
}
catch (Exception ex)
{
}
}
Is there something else that is needed ensure the function times out correctly? Please let me know if I can provide some more details.
Edit
I see the below message for checkCFOPTask.Result in the immediate window of Visual Studio-
Id = Cannot evaluate expression because the code of the current method is optimized., Status = Cannot evaluate expression because the code of the current method is optimized., Method = Cannot evaluate expression because the code of the current method is optimized., Result = Cannot evaluate expression because the code of the current method is optimized.
It looks like you will need to add time-out support to the method that you're calling - because if it doesn't find the thing it's looking for it will loop forever.
The easiest way to do this is to pass a CancellationToken to the method. You should also factor out the testing code into a separate method that returns bool.
Also note that you have a busy loop, which is generally not a good idea when polling! It's better to introduce a small sleep when polling if the thing you're polling for isn't available. (Note: Polling in general is not a good approach if you have a better way of checking for something, but it doesn't look like there's anything else you can use here, so polling will have to do.)
You can write your method like so (I've omitted the code that polls for the thing you're looking for in order to concentrate on the other logic):
private bool CheckCFOPExists(CancellationToken cancel)
{
TimeSpan retryDelay = TimeSpan.FromMilliseconds(500);
while (true)
{
if (tryToFindTheThing()) // Blocking call.
return true;
if (cancel.WaitHandle.WaitOne(retryDelay))
return false;
}
}
bool tryToFindTheThing()
{
return false; // Your implementation goes here.
}
Then to call this and have a 10-second timeout you would do something like this (compilable console app):
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static void Main()
{
var test = new Program();
// Create a cancellation token source that cancels itself after 10 seconds:
var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(10));
// Create and run the task:
var sw = Stopwatch.StartNew();
var checkCFOPTask = Task.Run(() => test.CheckCFOPExists(cancellation.Token));
Console.WriteLine("Waiting for task to finish.");
Console.WriteLine($"Task returned: {checkCFOPTask.Result} after {sw.ElapsedMilliseconds} ms");
}
private bool CheckCFOPExists(CancellationToken cancel)
{
TimeSpan retryDelay = TimeSpan.FromMilliseconds(500);
while (true)
{
if (tryToFindTheThing()) // Blocking call.
return true;
if (cancel.WaitHandle.WaitOne(retryDelay))
return false;
}
}
bool tryToFindTheThing()
{
return false; // Your implementation goes here.
}
}
}
Before using the Result you need to check if your task actually is completed with Task.IsCompleted.
if (checkCFOPTask.IsCompleted && checkCFOPTask.Result)

ContinueWith not waiting for task to complete

I have a function (below) that I retrieve data from an API. If I set a breakpoint at the line that deserializes it, then I can see that it is populated with data which is great.
When I continue on, it goes into the second function (below) and it throws an error. The error says next to it Not yet computed, and therefore throwing an exception.
When I do it with a small list it works just fine (I presume its cos it's a small set of data).
How is this possible when I'm using ContinueWith (waiting for the task to complete)?
public static async Task<Data> GetAllCardsInSet(string setName)
{
setName = WebUtility.UrlEncode(setName);
var correctUri = Path.Combine(ApiConstants.YugiohGetAllCardsInSet, setName);
Console.WriteLine();
using (var httpClient = new HttpClient())
{
var response =
await httpClient.GetAsync(correctUri);
var result = await response.Content.ReadAsStringAsync();
var cardData = JsonConvert.DeserializeObject<CardSetCards>(result);
for (int i = 0; i < cardData.Data.Cards.Count; i++)
{
cardData.Data.Cards[i] = FormatWords(cardData.Data.Cards[i]);
}
return cardData.Data;
}
}
private void GetYugiohCardsAndNavigate(string name)
{
var cardSetData = YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
});
}
Your GetAllCardsInSet method no need to change.
But using of this method can be refactored.
Method GetAllCardsInSet return Task and you not observed the completion of the this Task.
You need to check is Task completes succesfully, easiest approach to use await keyword. Awaiting task will unwrapp returned value or throw exception if task completed with exception.
For using async/await in the GetYugiohCardsAndNavigate change method signature to aynchronous and returning Task
private async Task GetYugiohCardsAndNavigate(string name)
{
try
{
var cardSetData = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
this.mainPage.NavigateToYugiohCardListPage(cardSetData);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App",
"Sorry, we could not fetch this set");
}
}
you called an async method in a sync method without Wait. It should have been done like:
YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
}).Wait();

Async Function Not Getting Called

Please forgive me for any noobish mistakes seen below, I'm learning some of the concepts I'm attempting to work with.
Problem:
While debugging my app, I was able to call an async function with Task.Start(). I felt that the app was in a working state for the phase I'm in so removed all breakpoints with CTRL + SHIFT + F9.
Once I ran the app with no breakpoints it would fail due to a property not getting populated. Now when I try to debug any breakpoint I set in the async function that handles most of the work is longer hit. It's like it is getting skipped. Can anyone see a reason why GetWowAuctionFileInfo isn't being called?
GetWowAuctionFileInfo is what is not getting called, or at least appears to be not getting called.
Thanks.
Relevant Code
Caller Function
private void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
Called Functions
public void StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
Task t = new Task(() => GetWowAuctionFileInfo(optionalUri));
t.Start();
//Func<string> function = new Func<string>(() => GetWowAuctionFileInfo(optionalUri));
//Task<string> tInfo = Task<string>.Factory.StartNew(() => GetWowAuctionFileInfo(optionalUri));
}
}
}
private async void GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
returnValue = ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
ReturnedData = returnValue;
}
private List<string> ConvertFileInfoToConsumableList(RealmJSFileCheck jsfc)
{
List<string> returnData = new List<string>();
if (jsfc.files.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.Append("File URL: ");
sb.Append(jsfc.files[0].url);
returnData.Add(sb.ToString());
sb = new StringBuilder();
sb.AppendLine("Last Modified: ");
sb.Append(jsfc.files[0].lastModified);
returnData.Add(sb.ToString());
}
else
{
returnData.Add("No File Info Found");
}
return returnData;
}
UPDATE
Thanks again all for the detailed commentary. I've gone through much documentation regarding Task usage and learned a lot in this exercise. I'm marking the answer from #Johnathon as the solution because it provided exactly what I was asking for and provided a very helpful link for more information.
Your GetWowAuctionFileInfo method is an asynchronous method, and you await an async call within it without returning a Task. In general it is bad practice to use async void. Instead, turn your GetWowAuctionFileInfo method into async Task<List<string>> GetWowAuctionFileInfo. This will let you await the GetAsync call, parse the data, and return the collection to the caller without having to use a ReturnObject.
private async Task<List<string>> GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
// You can just return the List<T> now.
return ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
}
Because the method was originally async void, you could not await the calling of it in your buttonTestJSFCHI_Click. Now that we've made it all Task based, you can await it within your event handler. Note that event handlers are generally the only acceptable place to use async void. Any time you are responsible for the creation of the methods, and not constrained by a contract (like event handlers), you should always return a Task on your async methods.
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
List<string> results = await w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in results)
{
textBoxResults.Text += res;
}
}
public async Task<List<string>> StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
// Since the GetWowAuctionFileInfo now returns Task, we don't need to create a new one. Just await the Task given back to us, and return the given result.
return await GetWowAuctionFileInfo(optionalUri);
}
}
}
The reason you saw the expected result while debugging is because the debug session was slow enough that the async operation completed in time for your code to use it. When running the app outside of the debugger, it runs faster than the async operation could complete, preventing you from seeing the data. Thus the need to await the entire async call stack, so you can prevent further execution from happening down that code-path until you receive all of the desired data.
Microsoft has a good write up on Task based programming, I'd take a read through it to help you understand it some.
EDIT
Just to clarify, when you return a Task<T> on your methods, you will be given the result when you await. For example:
List<string> result = await StartTask();
Even though StartTask returns Task<List<string>>, the await operation will wait for the StartTask() method to complete, and then unwrap the result from the Task<T> object and give you the result back automatically. So don't let the method signature fool you, if you await it, you will be given back the resulting data, and not the actual Task itself. There won't be any need for you to pull the data out of the Task manually.
Because you not waiting for result.
You loop with ReturnedData before it was assigned with data.
I think you don't need to create new Task at all. Make GetWowAuctionFileInfo method properly asynchronous which returns Task.
private async Task GetWowAuctionFileInfo(string auctionInfoUri)
{
// same code
}
Change StartTask to return Task. Because we not awaiting result here we don't need make method asynchronous.
Suggest to change name of this method to LoadData for example, which give more information about what this method does.
public Task StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
return GetWowAuctionFileInfo(optionalUri) // this will return Task
}
}
// if validation fails - return completed task or throw exception
return Task.CompletedTask;
}
Then you can call it in Button_Click event handler
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
await w.StartTask("FileInfo", "yourUrl");
// This line will be executed only after asynchronous methods completes succesfully
// or exception will be thrown
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}

Categories