execution of Dispatcher.BeginInvoke ends when reaches await statement - c#

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.

Related

Cancellation Token Doesn't throw exception

Good day.
I'm having a problem exiting a task with the cancellation token.
My program freezes when I get to the token2.ThrowIfCancellationRequested();.
Following it with the breakpoints is shows that the token2 is cancelled, but the program doesn't revert back to the previous sub routine where I try and catch
try
{
Task.Run(() => SendData_DoWork(_tokenSource3));
}
catch (OperationCanceledException ex)
{
SetText("Communivation error with device");
SetText("");
}
finally
{
token.Dispose();
}
}//comms routine
//send Meter Address to communicate to meter
private void SendData_DoWork(CancellationTokenSource token)
{
var token2 = token.Token;
var _tokenSource4 = new CancellationTokenSource();
try
{
timer.Interval = 10000;
timer.Start();
timer.Elapsed += OnTimerElapsed;
NetworkStream stream = client.GetStream();
SerialConverter serialConverter = new SerialConverter();
Thread.Sleep(1000);
string newtext = null;
newtext = $"/?{address}!\r\n";
SetText("TX: " + newtext);
byte[] newData = stringSend(newtext);
stream.Write(newData, 0, newData.Length);
Thread.Sleep(50);
byte[] message = new byte[23];
int byteRead;
while (true)
{
byteRead = 0;
try
{
byteRead = stream.Read(message, 0, 23);
if (message[0] == (char)0x15)
{
token.Cancel();
}
}
catch
{
token.Cancel();
}
if ((byteRead == 0))
{
token.Cancel();
}
timer.Stop();
timer.Dispose();
ASCIIEncoding encoder = new ASCIIEncoding();
string newresponse = encoder.GetString(serialConverter.convertFromSerial(message));
SetText("RX: " + newresponse);
if (newresponse[0].ToString() == SOH)
{
token.Cancel();
}
if (newresponse != null)
{
/* NEXT SUB ROUTINE*/
}
else { break; }
}//while looop
}//try
catch (Exception ex)
{
token.Cancel();
}
if (token2.IsCancellationRequested)
{
timer.Stop();
timer.Dispose();
token2.ThrowIfCancellationRequested();
}
}//sendData subroutine
You are launching a Task, and ignoring the result; the only time Task.Run would throw is if the task-method is invalid, or enqueuing the operation itself failed. If you want to know how SendData_DoWork ended, you'll need to actually check the result of the task, by capturing the result of Task.Run and awaiting it (preferably asynchronously, although if we're talking async, SendData_DoWork should probably also be async and return a Task).
Your catch/finally will probably be exited long before SendData_DoWork even starts - again: Task.Run just takes the time required to validate and enqueue the operation; not wait for it to happen.
I think you have missunderstood how cancellation tokens are supposed to work. Your work method should take a CancellationToken, not a CancellationTokenSource. And it should call ThrowIfCancellationRequested inside the loop, not after. I would suspect that you would get some issues with multiple cancel calls to the same cancellation token.
Typically you would use a pattern something like like this:
public void MyCancelButtonHandler(...) => cts.Cancel();
public async void MyButtonHandler(...){
try{
cts = new CancellationTokenSource(); // update shared field
await Task.Run(() => MyBackgroundWork(cts.Token));
}
catch(OperationCancelledException){} // Ignore
catch(Exception){} // handle other exceptions
}
private void MyBackgroundWork(CancellationToken cancel){
while(...){
cancel.ThrowIfCancellationRequested();
// Do actual work
}
}
So in my particular case it seems like changing the sub-routines from private async void ... to private async Task fixes the particular issue that I'm having.

Using Tasks in a non async function

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.

Best way to use multiple awaits C# .NET Blazor

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;
}

Problems working with async Task and Textbox.Text = "Hello"

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;
}
}
}});

when Task.IsCancelled is set as true?

when will come Task.IsCanceled = true;
Code:
var cts = new CancellationTokenSource();
string result = "";
cts.CancelAfter(10000);
try
{
Task t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
result = "success!";
}
cts.Token.ThrowIfCancellationRequested();
}, cts.Token);
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.IsRunning)
{
if (timer.ElapsedMilliseconds <= 10000)
{
if (result != ""){
timer.Stop();
Console.WriteLine(result);
}
}
else
{
timer.Stop();
//cts.Cancel();
//cts.Token.ThrowIfCancellationRequested();
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled); // still its appear in false.
}
My requirement is - Task is not completed upto 10seconds, Need to cancel the task.
So I am setting timer and watch upto the given seconds. its not completed mean cancel the task and showing error message.
You have to pass the token to your method. It should inspect the token and respect the call to Cancel() of the CancellationTokenSource.
Or you do it yourself:
Task t = Task.Factory.StartNew(() =>
{
myResult = method(); // Request processing in parallel
cts.Token.ThrowIfCancellationRequested(); // React on cancellation
}, cts.Token);
A complete example is this:
async Task Main()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
cts.CancelAfter(500);
Task t = null;
try
{
t = Task.Run(() => { Thread.Sleep(1000); ct.ThrowIfCancellationRequested(); }, ct);
await t;
Console.WriteLine(t.IsCanceled);
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
}
The output is that an OperationCanceledException is thrown and the result is
True
if you remove the ct.ThrowIfCancellationRequested(); part it will show
False
Edit:
Now, you have updated the question, some comments on that. First, you won't need the timer anymore since you are using the CancelAfter method. Second, you need to await your task. So that makes something like this:
string result = "";
cts.CancelAfter(10000);
Task t = null;
try
{
t = Task.Run(() =>
{
using (var stream = new WebClient().OpenRead("http://www.rediffmail.com"))
{
cts.Token.ThrowIfCancellationRequested();
result = "success!";
}
}, cts.Token);
await t;
}
catch (OperationCanceledException)
{
Console.WriteLine(t.IsCanceled);
}
This should show that t.IsCanceled is true but of course only when the call of the WebClient takes longer that 10 seconds.
From documentation:
A Task will complete in the TaskStatus.Canceled state under any of the following conditions:
Its CancellationToken was marked for cancellation before the task started executing.
The task acknowledged the cancellation request on its already signaled CancellationToken by throwing an OperationCanceledException that bears the same CancellationToken.
The task acknowledged the cancellation request on its already signaled CancellationToken by calling the ThrowIfCancellationRequested method on the CancellationToken.
So basically you would need to throw an OperationCanceledException within your task to force the state for instance by executing cts.Token.ThrowIfCancellationRequested() just after you cancel it.
But the intention of this mechanism is a bit the other way around. You cancel source say while user presses cancel button on your form (from outside of your task) an task just verifies if cancellation was requested in some safe to cancel points of its code.

Categories