I am implementing a run() function that can not be async. The goal is to send a get request to a server, and then download some files based on the result of the get request and return the number of files downloaded. I have written an async function to do that but I want to essentially "await" it before the rest of my main function continues. I am unable to achieve this behavior currently as the function just hangs. Not sure why its hanging :(
I think I just need some insights into Task and why this isn't working as expected. I am familiar with promises in JS so I thought this wouldn't be that difficult.
Thank you!
public int run(){
FilesManager test = new FilesManager();
string path = Path.Combine("C:\Users\username\Documents", "Temp");
Task<int> T_count = test.Downloadfiles(path); //TODO: trying to "await" this before the messageBoxes
Task.WaitAll(T_count);
int count = T_count.Result;
MessageBox.Show("File Downloaded");
MessageBox.Show(count.ToString());
}
public async Task<int> Downloadfiles(string path)
{
String[] response = await getClient.GetFromJsonAsync<string[]>("http://localhost:3000");
int counter = 0;
try
{
foreach (string url in response)
{
Uri uri = new Uri(url);
var response2 = await getClient.GetAsync(uri);
using (var fs = new FileStream(
path + counter.ToString(),
FileMode.Create))
{
await response2.Content.CopyToAsync(fs);
}
counter++;
}
return counter;
}catch(Exception e)
{
while (e != null)
{
MessageBox.Show(e.Message);
e = e.InnerException;
}
return 0;
}
}
EDIT:
Still not able to get the task.WaitAll(T_count) to work. With some more debugging, the execution jumps from the response2 = await getClient.GetAsync... straight into the waitAll, never hitting the copyToAsync or counter++.
Sync-over-async is a fundamentally difficult problem, because you need to guarantee that continuations never try to run on the thread you are blocking on, otherwise you will get a deadlock as you have seen. Ideally you would never block on async code, but sometimes that is not possible.
Task.Run(...)..GetAwaiter().GetResult() is normally fine to use for this purpose, although there are still some circumstances when it can deadlock.
Do not call the UI from inside the async function, therefore you must move the catch with MessageBox.Show to the outer function.
You can also make this more efficient, by using HttpCompletionOption.ResponseHeadersRead, and you are missing a using on the response2 object.
public int run()
{
FilesManager test = new FilesManager();
string path = Path.Combine("C:\Users\username\Documents", "Temp");
try
{
int count = Task.Run(() => test.Downloadfiles(path)).GetAwaiter().GetResult();
MessageBox.Show("File Downloaded");
MessageBox.Show(count.ToString());
return count;
}
catch(Exception e)
{
while (e != null)
{
MessageBox.Show(e.Message);
e = e.InnerException;
}
return 0;
}
}
public async Task<int> Downloadfiles(string path)
{
String[] response = await getClient.GetFromJsonAsync<string[]>("http://localhost:3000");
int counter = 0;
try
{
foreach (string url in response)
{
using (var response2 = await getClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
using (var fs = new FileStream(
path + counter.ToString(),
FileMode.Create))
{
await response2.Content.CopyToAsync(fs);
}
counter++;
}
return counter;
}
}
Another option is to remove and afterwards restore the SynchronizationContext, as shown in this answer.
Related
In a Blazor app I have a file uploader that can take multiple files. When the user clicked 'upload' I used something like this:
private async Task HandleFileUploadClicked()
{
_isUploading = true;
StateHasChanged();
foreach (var file in _files)
{
await using var stream = file.OpenReadStream(file.Size);
await _myService.AddFile(
stream
);
}
_isUploading = false;
_files = new List<IBrowserFile>();
await LoadData();
StateHasChaged();
}
But I discovered this doesn't work correct. Because I am not using the result of _myService.Addfile the call is just fired and the code continues at _isUploading = false;
Then I thought maybe I should keep an array of the AddFile Tasks and use Task.WaitAll to wait for all of them. But the problem it that this waits on the UI thread, so _isUploading = true; and StateHasChanged have no effect.
My current (working) solution is something like this:
private async Task HandleFileUploadClicked()
{
_isUploading = true;
StateHasChanged();
var allOk = true;
foreach (var file in _files)
{
await using var stream = file.OpenReadStream(file.Size);
var result = await _myService.AddFile(
stream
);
allOk = result != null && allOk;
}
_isUploading = false;
_files = new List<IBrowserFile>();
await LoadData();
StateHasChaged();
}
But this feels like a hack.
Is there a better way to wait for multiple tasks but without blocking the UI thread?
You can make use of Task Parller Library method Task.WhenAll(). Which will make sure that the tasks are processed in parallel.
code snippet should be as follows:
bool allOk = true;
try
{
var uploadtasks = new List<Task>();
foreach(var file in filesToUpload)
{
uploadtasks.Add(_myService.AddFile(file));
}
await Task.WhenAll(uploadtasks);
}
catch(Exception ex)
{
allOk = false;
}
First of all, sorry because I am so new at C# and I decided to make this question because I have been choked in this for hours.
I have an GUI that works with Google Cloud Speech services and make a Speech-to-Text operation. I share with you the whole method that runs when a button is clicked:
private async Task<object> StreamingMicRecognizeAsync(int seconds)
{
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
Console.WriteLine("No microphone!");
return -1;
}
GoogleCredential googleCredential;
using (Stream m = new FileStream(#"..\..\credentials.json", FileMode.Open))
googleCredential = GoogleCredential.FromStream(m);
var channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host,
googleCredential.ToChannelCredentials());
var speech = SpeechClient.Create(channel);
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 48000,
LanguageCode = "es-ES",
},
InterimResults = true,
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(48000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
// Print responses as they arrive.
Task printResponses = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Console.WriteLine(alternative.Transcript);
//Textbox1.Text = alternative.Transcript;
}
}
}
});
waveIn.StartRecording();
Console.WriteLine("Speak now.");
Result_Tone.Text = "Speak now:\n\n";
await Task.Delay(TimeSpan.FromSeconds(seconds));
// Stop recording and shut down.
waveIn.StopRecording();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await printResponses;
return 0;
}
My problem is that I want to update the content of the Textbox1control but it doesn´t work. It writes perfectly the output into the console with the line Console.WriteLine(alternative.Transcript); but not into my textbox.
If someone could help I would appreciate so much his help.
The problem is that you're using Task.Run, which means your code will be running on a thread-pool thread.
Instead of calling Task.Run(), just move that code into a separate async method:
async Task DisplayResponses(IAsyncEnumerator<StreamingRecognizeResponse> responses)
{
while (await responses.MoveNext(default(CancellationToken)))
{
foreach (var result in responses.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}
}
Then call that method directly (without Task.Run) from code that's already on the UI thread (e.g. an event handler).
The async machinery will make sure that after the await expression, you're back on the UI thread (the same synchronization context). So the assignment to the Text property will occur on the UI thread, and all should be well.
For example:
// This would be registered as the event handler for a button
void HandleButtonClick(object sender, EventArgs e)
{
var stream = client.StreamingRecognize();
// Send the initial config request
await stream.WriteAsync(...);
// Presumably you want to send audio data...
StartSendingAudioData(stream);
await DisplayResponses(stream.ResponseStream);
}
Tasks run on seperate threads, so you must Invoke an action that will be performed on the control's thread
Textbox1.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
Edit: For WPF, I believe the equivalent is
Textbox1.Dispatcher.Invoke(new Action(() =>
{
Textbox1.Text= "";
}));
have you tried using Dispatcher.InvokeASync()?
await Dispatcher.InvokeAsync(() => {while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
Textbox1.Text = alternative.Transcript;
}
}
}});
I have a code which should take all pictures from folder, put them into object named "PhotoInspection", add some informations and put this object into list. See the code below
private async void btnSend_Click(object sender, RoutedEventArgs e)
{
bool isOn = tsOnOff.IsOn;
TextBlock tbChosen = new TextBlock();
tbChosen = lbInspections.SelectedItem as TextBlock;
string chosen = tbChosen.Text;
AllInspectionPhotos aip = new AllInspectionPhotos();
var folders = await ApplicationData.Current.LocalFolder.GetFoldersAsync();
if (isOn)
{
foreach (var item in folders)
{
if (item.Name == "ONLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photos = await inspectionFolder.GetFilesAsync();
foreach (var photo in photos)
{
using (Stream stream = await photo.OpenStreamForReadAsync())
{
PhotoInspection phtInsp = new PhotoInspection();
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
aip.Photos.Add(phtInsp);
}
}
}
}
}
}
}
else
{
foreach (var item in folders)
{
if (item.Name == "OFFLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photoset = await inspectionFolder.GetFilesAsync();
foreach (var photo in photoset)
{
PhotoInspection phtInsp = new PhotoInspection();
using (Stream stream = await photo.OpenStreamForReadAsync())
{
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
}
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//await App._client.SendPhotoAsync(phtInsp);
aip.Photos.Add(phtInsp); //fires error here
});
}
}
}
}
}
}
await App._client.SendAllPhotosAsync(aip);
}
However when I try to add object into list, I get "attempted to read write protected memory" error. PhotoInspection object is filled with relevant data and everything looks good before adding to list. Thanks for any help
The problem is due to the Dispatcher.RunAsync call, which returns control to the calling routine as soon as the aip.Photos.Add() lambda is queued, and not when it has completed. This results in the aip object being disposed before the lambda has actually run.
The MSDN page for CoreDispatcher.RunAsync says:
[...] it schedules the work on the UI thread and returns control to the caller immediately.
and:
To spin off a worker thread from the UI thread, do not use this method (CoreDispatcher::RunAsync). Instead, use one of the Windows::System::Threading::ThreadPool::RunAsync method overloads.
It also provides the following code example, and the comment is from Microsoft!
//DO NOT USE THIS CODE.
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await signInDialog.ShowAsync();
});
// Execution continues here before the call to ShowAsync completes.
I am getting lots of delay when saving data in database. I have one exe (Deskptop Application) which reads data from serial port and push that entry in to database through web API service but my application get hangs on this line:
httpClient.PostAsync("api/MyController/Save", httpConent).Result;
This exe is responsible to call my web API service method and save data to my database.
This is my code:
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
// Send data to whom ever interested
if (NewSerialDataRecieved != null)
{
NewSerialDataRecieved(this, new SerialDataEventArgs(data));
}
}
void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
{
if (this.InvokeRequired)
{
// Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
//// Fired-off asynchronously; let the current thread continue.
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
return;
}
//data is converted to text
string str = Encoding.ASCII.GetString(e.Data);
if (!string.IsNullOrEmpty(str))
{
CallWebservice(str)
}
}
public void CallWebservice(string xmlRequest)
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("WebService Url");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
StringContent httpConent = new StringContent(xmlRequest, Encoding.UTF8);
HttpResponseMessage responseMessage = null;
try
{
responseMessage = httpClient.PostAsync("api/MyController/Save", httpConent).Result;
}
catch (Exception ex)
{
if (responseMessage == null)
{
responseMessage = new HttpResponseMessage();
}
responseMessage.StatusCode = HttpStatusCode.InternalServerError;
responseMessage.ReasonPhrase = string.Format("RestHttpClient.SendRequest failed: {0}", ex);
}
}
}
My web api method:
public async Task<HttpResponseMessage> Save(HttpRequestMessage request)
{
var requestdata = request.Content.ReadAsStringAsync().Result;//extract Users Id's from this
var users=context.User.Where(t => (t.Stats == userId1) || (t.Stats == userId2)).ToList();
var objUsersMapping= new UsersMapping();
objUsersMapping.Work1 = users[0].Work1;
objUsersMapping.Work2 = users[1].Work1;
await this.SaveUsersMapping(objUsersMapping);
}
public async Task<UsersMapping> SaveUsersMapping(UsersMapping objUsersMapping)
{
using (var context = new MyEntities())
{
try
{
context.UsersMapping.Add(objUsersMapping);
await context.SaveChangesAsync();
return objUsersMapping;
}
catch (Exception foExe)
{
return null;
}
}
}
I haven't work much on Windows application so I am not understanding why my application is hanging.
Note: data will be continuously coming to my serial port so saving data through web service should not disturb _serialPort_DataReceived event.
This is a summary of my comments beneath the OP's question
You are calling an asynchronous method synchronously. That will cause the current thread to block. Get rid of the .Result and alter the rest of the code accordingly (like including async and await there too).
e.g. change this line
responseMessage = httpClient.PostAsync("api/MyController/Save", httpConent).Result;
...to:
responseMessage = await httpClient.PostAsync("api/MyController/Save", httpConent);
Your method signature will need to be changed as follows:
public async Task CallWebservice(string xmlRequest)
{
}
Any method that calls it will also need to be async and use await for example your _spManager_NewSerialDataRecieved() method.
Note it has been changed from void to async void. Note too the await prior to CallWebservice().
async void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
{
if (this.InvokeRequired)
{
// Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
//// Fired-off asynchronously; let the current thread continue.
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
return;
}
//data is converted to text
string str = Encoding.ASCII.GetString(e.Data);
if (!string.IsNullOrEmpty(str))
{
await CallWebservice(str)
}
}
A note on async void
Because the above method is an event handler it is fine for the method to be async void. Generally you want to avoid async void in non event handler code. For more info see this brilliant article by Mr Stephen Cleary.
Is this the only problem sir??
You should fix your async Save() method on the server too as it also has a .Result(). That will block the current thread on the server. Prefix it with a await. Generally you want to avoid .Result as a means to wait for the task to complete. It is safe to use as a means to obtain the result after you have awaited it, but there are more elegant ways to await and get the result in a single line of code. e.g. x = await FooAsync();.
Following code is executed in ScheduledAgent on WindowsPhone device, but for the reason that I do not know, execution ends when reaches this line:
col = await wl.GetModelsAsync(day, 1, mgr.LastCountryPath, Resolution.PhoneResolutions[3]);
this method calls the method below and execution ends on 2nd line of that method.
public async Task<Stream> DownloadAsync(string url)
{
WebRequest rq = WebRequest.Create(url);
WebResponse rp = await Task.Factory.FromAsync<WebResponse>(rq.BeginGetResponse, rq.EndGetResponse, null);
return rp.GetResponseStream();
}
All the code was working properly until today, but as far as I remember, I haven't changed much.
Deployment.Current.Dispatcher.BeginInvoke((async () =>
{
List<WallpaperModel> col;
try
{
int day = 0;
if (mgr.ImageMode == WallpaperChangeMode.RandomMode)
{
Random r = new Random();
day = r.Next(16);
}
col = await wl.GetModelsAsync(day, 1, mgr.LastCountryPath, Resolution.PhoneResolutions[3]);
}
catch (Exception)
{
NotifyComplete();
return;
}
Stream s = await wl.DownloadAsync(col[0].Image.UriSource.OriginalString);
var hlpr = new LockHelper(s);
await hlpr.TrySetLockAsync(true);
}));
Try awaiting on InvokeAsync instead of BeginInvoke:
await Deployment.Current.Dispatcher.InvokeAsync((async () =>
{
List<WallpaperModel> col;
try
{
int day = 0;
if (mgr.ImageMode == WallpaperChangeMode.RandomMode)
{
Random r = new Random();
day = r.Next(16);
}
col = await wl.GetModelsAsync(day, 1, mgr.LastCountryPath, Resolution.PhoneResolutions[3]);
}
catch (Exception)
{
NotifyComplete();
return;
}
Stream s = await wl.DownloadAsync(col[0].Image.UriSource.OriginalString);
var hlpr = new LockHelper(s);
await hlpr.TrySetLockAsync(true);
}));
Dispatcher.BeginInvoke() basically adds commands(adding textblock text ect..) to the Ui thread Queue. await if fruitful when you use it on threads/Tasks. even though you use await on Dispatcher.BeginInvoke() its not gonna do anything except it just adds the content of the deleage to the Ui Thread.
i would suggest put all your business logic in the thread and await the thread. once your thread gets executed successfully, use dispatcher to update the UI thread.
basically don't mix up your task/thread and dispatcher.