This is my first attempt to get code to run async and I can't tell if it actually is. The function download report does not have an "await" and there is a warning saying it will run synchronously.
I am attempting to download multiple reports at the same time and then zip them all into one file.
The result is as expected but I would like to be certain that the code is actually performing in async.
static async Task Main(string[] args)
{
string folderName = "Batch123";
string fullDir = Path.Combine(ConfigurationManager.AppSettings["path"], folderName);
Directory.CreateDirectory(fullDir);
await RunReports(folderName);
string zipPath = Path.Combine(ConfigurationManager.AppSettings["path"], "Zip", folderName);
Directory.CreateDirectory(zipPath);
ZipFile.CreateFromDirectory(fullDir, Path.Combine(fullDir, zipPath, "CRAs.zip"));
}
private static async Task RunReports(string folderName)
{
string[] dunsToProcess = new string[] {"d1"
,"d2"
,"d3"
};
await Task.WhenAll(dunsToProcess.Select(i => DownloadReport(i, folderName)));
}
private static async Task DownloadReroport(string DUNS, string folderName)
{
NetworkCredential cred = new NetworkCredential(ConfigurationManager.AppSettings["networkUser"]
, ConfigurationManager.AppSettings["networkPassword"]);
string fullPath = Path.Combine(ConfigurationManager.AppSettings["path"], folderName, string.Format("CRA for DUNS {0}.pdf", DUNS));
WebClient wc = new WebClient();
wc.Credentials = cred;
wc.DownloadFile(#"http://xxxxxxx&pcDUNS=" + DUNS
, fullPath);
}
I hope it is right as it will be the basis of a lot of other changes. If not, can you point out what I am doing wrong.
Feel free to ridicule anything with my code!!! I have had no c# training at all.
Thank you.
I believe you are referring and threads and tasks and trying to find something async.
As First, I am not sure what you are doing and how this even works when your main method is Task, this make no sense to me, so, try following :
let main to be as this is default, there is nothing to do in this declaration change
made class which is doing what you want to do (downloads probably)
then made Tasks or threads to call many tasks or threads in time. Keep in mind that there is no point to made to many of them, you may want to limit number of tasks/threads executing in time. You may want to have all tasks identified, or you may want just to wait for all of them to be executed, your need and decision, but Task have properites IsCompleted, IsCanceled, etc ... and if you are generating threads you may want to investigate following classes and methods :
ManualResetEvent
WaitHandle
As short resume, you approach is wrong if I am even able to understand what you are trying to do in this code
Related
Background
I have an MVC 5 application and wanted to test if the requests were running in parallel. To do so I used the code below, and opened multiple pages all making the same request.
Code
Below is a relatively simple method where I wanted to the parallel nature.
public async Task<ActionResult> Login(string returnUrl, string message = "")
{
var rng = new Random();
var wait = rng.Next(3, 10);
var threadGuid = Guid.NewGuid();
DebugHelper.WriteToDebugLog($"Thread {threadGuid} about to wait {wait} seconds");
await Task.Delay(wait * 1000);
DebugHelper.WriteToDebugLog($"Thread {threadGuid} finished");
return View();
}
The class DebugHelper is just used so that I can write to a file safely.
public static class DebugHelper
{
private static readonly object WriteLock = new object();
public static void WriteToDebugLog(string message, string path = "C:\\Temp\\Log.txt")
{
lock (WriteLock)
{
File.AppendAllLines(path, new string[] { "", GetDateString(), message });
}
}
}
Output
I'm consistently getting this type of output which suggests the threads are blocking each other.
2020-03-24T13:43:43.1431913Z
Thread 6e42a6c5-d3cb-4541-b8aa-34b290952973 about to wait 7 seconds
2020-03-24T13:43:50.1564077Z
Thread 6e42a6c5-d3cb-4541-b8aa-34b290952973 finished
2020-03-24T13:43:50.1853278Z
Thread 90923f55-befd-4224-bdd8-b67f787839fc about to wait 4 seconds
2020-03-24T13:43:54.1943271Z
Thread 90923f55-befd-4224-bdd8-b67f787839fc finished
2020-03-24T13:43:54.2312257Z
Thread fa2d8d30-b762-4262-b188-0b34da5f4f04 about to wait 3 seconds
2020-03-24T13:43:57.2370556Z
Thread fa2d8d30-b762-4262-b188-0b34da5f4f04 finished
2020-03-24T13:43:57.2679690Z
Thread 37311a0e-d19e-4563-b92a-5e5e3def379a about to wait 8 seconds
2020-03-24T13:44:05.2812367Z
Thread 37311a0e-d19e-4563-b92a-5e5e3def379a finished
Question
Why is this occurring?
I was under the impression that any ASP.NET application was multithreaded to begin with, so even in a situation where I don't have the async/await setup, I thought it would run these threads simultaneously.
Update
As suggested in the answers/comments, my methodology was wrong. After using the following code I could see quite clearly in the logs that it was indeed running in parallel.
var targetTime = DateTime.UtcNow + TimeSpan.FromSeconds(5);
while(DateTime.UtcNow < targetTime)
{
DebugHelper.WriteToDebugLog($"Thread {threadGuid} with ID {threadId} doing stuff");
await Task.Delay(1000);
}
It simply boils down to the fact that your debug logging with its WriteLock and synchronous File.AppendAllLines forces a synchronization lock onto all asynchronous functions that call it.
You would do far better to have an asynchronous write to debug process that would allow your tasks to continue running.
Product/consumer patter, semaphores, events, use of asynchronous file access APIs all spring to mind.
If you are using session at all it can lock the user to a single thread. Check for controller level, page level, or filter/attribute session use. If you are unsure try adding
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
to the controller.
Also, await by default will continue on the same thread that began the await. Try using configureAwait(false) to allow it to be flexible in the threads it uses.
await Task.Delay(wait * 1000).ConfigureAwait(false);
I have been making a client that installs a program I am also making. The problem is when I go to download the files. Sometimes, it gets stuck. I get thrown a error stating
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
...and my UI freaks out (Buttons go blank, labels disapper, images go missing, ect). I have researched this error and it seems it happens with images, but I am downloading zip files that contain no images. I have found that it might have something to do with 5 files I am extracting from the zip files but thats not entirely accurate because it doesnt always happen and I have no real way to determine EXACTLY whats causing it. I suspect its because I cannot download so much in a short period of time but I do not know if this is why exactly either.
Also to add to this, the files still complete downloading when in debug mode, they ever continue through the async process, awaiting properly and everything.
I have tried narrowing down what file is causing it but I dont have any evidence to support it is a specific file. I have also tried spliting up the zip files to see if its the size of how much Im downloading at once, still no luck.
These are the download functions.
The RunWorkerTaskAsync() is a custom reference I created to allow a worker to be "awaited". I privide the code below.(I take no credit as its pieces of code I have pulled from others)
private async Task DownloadLibs()
{
Response.Text = "Updating Libraries...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Libraries.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Libraries.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Libraries...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
private async Task DownloadMods()
{
Response.Text = "Updating Mods (1/2)...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Mods.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Mods.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Mods (1/2)...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
private async Task DownloadExtras()
{
Response.Text = "Updating Mods (2/2)...";
this.Update();
string url = #"http://akumamc.com/AkumaMC/Mods2.zip";
if (!string.IsNullOrEmpty(url))
{
Uri uri = new Uri(url);
string fileName = System.IO.Path.GetFileName(uri.AbsolutePath);
await DLclient.DownloadFileTaskAsync(uri, #"C:\temp\" + fileName);
DLclient.Dispose();
}
FileZipName = #"C:\temp\Mods2.zip";
FileZipPath = #"C:\temp\.minecraft";
Response.Text = "Extracting Mods (2/2)...";
this.Update();
await extractFile.RunWorkerTaskAsync();
}
RunWorkerTaskAsync:
public static Task<object> RunWorkerTaskAsync(this BackgroundWorker backgroundWorker)
{
var tcs = new TaskCompletionSource<object>();
RunWorkerCompletedEventHandler handler = null;
handler = (sender, args) =>
{
if (args.Cancelled)
tcs.TrySetCanceled();
else if (args.Error != null)
tcs.TrySetException(args.Error);
else
tcs.TrySetResult(args.Result);
};
backgroundWorker.RunWorkerCompleted += handler;
try
{
backgroundWorker.RunWorkerAsync();
}
catch
{
backgroundWorker.RunWorkerCompleted -= handler;
throw;
}
return tcs.Task;
}
I expect the files to download without the form causing UI glitches and crashing.
EDIT: Link to author's client code (taken from comment below)
This is a summary of my comments beneath the OP's question
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
So the "interop" error implies some form of component object model (COM) problem and the things that stick out are:
the use of some 3rd party library that may be using COM
your RunWorkerTaskAsync extension method seems to be making multiple calls to BackgroundWorker.RunWorkerAsync without first checking that the worker is busy.
BackgroundWorker.RunWorkerAsync() is a void, it does not return a Task and so can't be used in async/await. Therefore your extension method is essentially kicking off a background worker without waiting for it to complete. Your extension method RunWorkerTaskAsync() (which isn't entirely async) returns immediately to those that called it.
You need to for the worker to complete before calling RunWorkerAsync again.
A possible fix:
Inside your extension method, check BackgroundWorker.IsBusy before telling it to run. (A better way is to wait for RunWorkerCompleted and kicking off a new one there)
Call RunWorkerAsync
Because you want to wait for this "task" to complete before returning control to say DownloadMods(), your extension method will need to monitor RunWorkerCompleted. This is kinda ugly as it goes against the original best practices of BackgroundWorker where everything is event-driven.
Alternatives
Considering you are using async/await anyway, why use BackgroundWorker at all? Consider wrapping up the essence of your extension method into a new method and call it via Task.Run().
You can still have a async Task ExtractFilesAsync method that runs in a child task (because we used Task.Run() it will also be a child thread) can report progress.
Something like (pseudo code):
await Task.Run ( async () => await
UnzipFilesAsync ( p =>
{
myProgressBar.BeginInvoke (new Action( () =>
myprogressBar.Progress = p; ));
});
.
.
.
UnzipFilesAsync (Action<int> progressCallback)
{
.
.
.
int percent = ...;
progressCallback (percent);
}
Tell me more about async progress bar updates
By the way, you shouldn't call MessageBox.Show or update the UI directly in a child thread, even if the call is a dialog with its own message pump.
I have a simple method that works in an asynchronous format, but when I try to change it to a synchronous format, it acts like it deadlocks and never returns anything. I know it is more effective to use asynchronous calls but I am curious as to why this is happening. Thanks in advance.
This returns a working result:
public static async Task getCompanyAsync(){
var client = new getRestClient();
var response = await companiesApi.GetCompaniesAsync(client);
var companies = response.GetResultAsync<List<Company>>();
}
This will make the program sit and spin until I kill it:
public static void getCompany(){
var client = new getRestClient();
var response = companiesApi.GetCompanies(client);
var companies = response.GetResult<List<Company>>();
}
Edit:
So here is some more detailed code.
static void Main(string[] args)
{
MainAsync(args).Wait();
Console.ReadLine();
}
public static async Task MainAsync(string[] args)
{
await getCompanyAsync();
getCompany();
}
private static async Task getCompanyAsync()
{
var client = getApiClient();
var companiesApi = new CompaniesApi(client);
var response = await companiesApi.GetCompaniesAsync();
var companyies = await response.GetResultAsync<List<Company>>();
foreach (var company in companyies)
{
Console.WriteLine(company.Name);
}
}
private static void getCompany()
{
var client = getApiClient();
var companiesApi = new CompaniesApi(client);
var response = companiesApi.GetCompanies();
var companyies = response.GetResult<List<Company>>();
foreach (var company in companyies)
{
Console.WriteLine(company.Name);
}
I the application ran as expected when I moved getCompany(); from the MainAsync() method into the Main() method.
Two points here.
First, async will often work in unexpected ways in a console application (as compared to a UI application). This is because UIs have a "built in" synchronization contexts (and, obviously, because they don't automatically exit after their "main" method equivalent has completed execution), whereas Console applications don't. See this article for directions on how to use a context in a console application to mitigate this.
Secondly, as pointed out in one of Stephen Cleary's excellent articles on the topic, it's a bad practice to block on async if avoidable as it can lead to exactly the kinds of deadlocks you describe. Basically, one way of solving this is to use async "all the way down" - i.e. use async/await instead of GetResult() in your second sample. This idea is echoed in the Microsoft documentation of best practices for async, which recommend against mixing synchronous and asynchronous code like you do in the second code sample.
In the article I linked to the author explains how a deadlock can occur when you mix synchronous and asynchronous code. Basically, this can occur when, for example, the async method is waiting for the same context as the GetResults() call is. (His article explains it a lot better than me though :) ).
I have a requirement, is to process X number of files, usually we can receive around 100 files each day, is a zip file so I have to open it, create a stream then send it to a WebApi service which is a workflow, this workflow calls two more WebApi Steps.
I implemented a console application that loops through the files then calls a wrapper which makes a REST call using HttpWebRequest.GetResponse().
I stressed tested the solution and created 11K files, in a synchronous version it takes to process all the files around 17 minutes, but I would like to create an async version of it and be able to use await HttpWebRequest.GetResponseAsync().
Here is the Async version:
private async Task<KeyValuePair<HttpStatusCode, string>> REST_CallAsync(
string httpMethod,
string url,
string contentType,
object bodyMessage = null,
Dictionary<string, object> headerParameters = null,
object[] queryStringParamaters = null,
string requestData = "")
{
try
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("some url");
req.Method = "POST";
req.ContentType = contentType;
//Adding zip stream to body
var reqBodyBytes = ReadFully((Stream)bodyMessage);
req.ContentLength = reqBodyBytes.Length;
Stream reqStream = req.GetRequestStream();
reqStream.Write(reqBodyBytes, 0, reqBodyBytes.Length);
reqStream.Close();
//Async call
var resp = await req.GetResponseAsync();
var httpResponse = (HttpWebResponse)resp as HttpWebResponse;
var responseData = new StreamReader(resp.GetResponseStream()).ReadToEnd();
return new KeyValuePair<HttpStatusCode,string>(httpResponse.StatusCode, responseData);
}
catch (WebException webEx)
{
//something
}
catch (Exception ex)
{
//something
}
In my console Application I have a loop to open and call the async (CallServiceAsync under the covers calls the method above)
foreach (var zipFile in Directory.EnumerateFiles(directory))
{
using (var zipStream = System.IO.File.OpenRead(zipFile))
{
await _restFulService.CallServiceAsync<WorkflowResponse>(
zipStream,
headerParameters,
null,
true);
}
processId++;
}
}
What end up happening was that only 2K of 11K got processed and didn't throw any exception so I was clueless so I changed the version I am calling the async to:
foreach (var zipFile in Directory.EnumerateFiles(directory))
{
using (var zipStream = System.IO.File.OpenRead(zipFile))
{
tasks.Add(_restFulService.CallServiceAsync<WorkflowResponse>(
zipStream,
headerParameters,
null,
true));
}
}
}
And have another loop to await for the tasks:
foreach (var task in await System.Threading.Tasks.Task.WhenAll(tasks))
{
if (task.Value != null)
{
Console.WriteLine("Ending Process");
}
}
And now I am facing a different error, when I process three files, the third one receives:
The client is disconnected because the underlying request has been completed. There is no longer an HttpContext available.
My question is, what i am doing wrong here? I use SimpleInjector as IoC would it be this the problem?
Also when you do WhenAll is waiting for each thread to run? Is not making it synchronous so it waits for a thread to finish in order to execute the next one? I am new to this async world so any help would be really much appreciated.
Well for those that added -1 to my question and instead of providing some type of solution just suggested something meaningless, here it is the answer and the reason why specifying as much detail as possible is useful.
First problem, since I'm using IIS Express if I'm not running my solution (F5) then the web applications are not available, that happened to me sometimes not always.
The second problem and the one giving me a huge headache is that not all the files got processed, I should've known the reason of this issue before, is the usage of async - await in a console application. I forced my console app to work with async by doing:
static void Main(string[] args)
{
System.Threading.Tasks.Task.Run(() => MainAsync(args)).Wait();
}
static async void MainAsync(string[] args)
{
//rest of code
Then if you note in my foreach I had await keyword and what was happening is that by concept await sends back the control flow to the caller, in this case the OS is the one calling the Console App (that is why doesn't make too much sense to use async - await in a console app, I did it because I mistakenly used await by calling an async method).
So the result was that my process only processed some X number of files, so what I end up doing is the following:
Add a list of tasks, the same way I did above:
tasks.Add(_restFulService.CallServiceAsync<WorkflowResponse>(....
And the way to run the threads is (in my console app):
ExecuteAsync(tasks);
Finally my method:
static void ExecuteAsync(List<System.Threading.Tasks.Task<KeyValuePair<HttpStatusCode, WorkflowResponse>>> tasks)
{
System.Threading.Tasks.Task.WhenAll(tasks).Wait();
}
UPDATE: Based on Scott's feedback, I changed the way I execute my threads.
And now I'm able to process all my files, I tested it and to process 1000 files in my synchronous process took around 160+ seconds to run all the process (I have a workflow of three steps in order to process the file) and when I put my async process in place it took 80+ seconds so almost half of the time. In my production server with IIS I believe the execution time will be less.
Hope this helps to anyone facing this type of issue.
I'm writing the video converting tool for one web site.
Logic is next
User upload the file
System conver it for user (of course user should not wait while converting)
I have a code:
public async void AddVideo(HotelVideosViewModel model)
{
var sourceFile = FileHandler.SaveFile(model.VideoFile);
var fullFilePath = HttpContext.Current.Server.MapPath(sourceFile);
await Task.Factory.StartNew(() => video.Convert(fullFilePath));
model.FileLocation = sourceFile;
model.IsConverted = true;
this.Add<HotelVideos>(Mapper.Map<HotelVideosViewModel, HotelVideos>(model));
}
This start encoding:
await Task.Factory.StartNew(() => video.Convert(fullFilePath));
But the code after this is never executed... Does somebody has a solution?
PS: under video.Conver(...) is have something like:
public bool Convert(string fileLocation)
{
// do staff
}
--- UPDATE ---
Just got the interesting thing. If i'm going to exchange video.Convert(fullFilePath) to Thread.Sleep(2000) everything start to work. So the issue is in Second method i think.
So i'm adding the next file code (draft now):
using Softpae.Media;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class VideoConvertManager
{
private readonly Job2Convert jobConverter = new Job2Convert();
private readonly MediaServer mediaServer = new MediaServer();
public bool Convert(string fileLocation)
{
this.jobConverter.pszSrcFile = fileLocation;
this.jobConverter.pszDstFile = fileLocation + ".mp4";
this.jobConverter.pszDstFormat = "mp4";
this.jobConverter.pszAudioCodec = null;
this.jobConverter.pszVideoCodec = "h264";
if (this.mediaServer.ConvertFile(this.jobConverter))
{
FileHandler.DeleteFile(fileLocation);
return true;
};
FileHandler.DeleteFile(fileLocation);
return false;
}
}
You need to make AddVideo return Task, and await its result before completing the ASP.NET request.
Update: DO NOT call Wait or other blocking methods on an asynchronous Task! This causes deadlock.
You may find my async intro helpful.
P.S. async doesn't change the way HTTP works. You still only have one response per request. If you want your end user (e.g., browser) to not block, then you'll have to implement some kind of queue system where the browser can start video conversion jobs and be notified of their result (e.g., using SignalR). async won't just magically make this happen - it only works within the context of a single request/response pair.
Adding ConfigureAwait(false) will keep the continuation from being forced on the request thread