C# and Tasks - UI Thread Hang - Pre-Async/Await keywords - c#

I'm trying to understand what the correct code to grab a set of data asynchronously when I do not have access to the client lib I am using to retrieve the data. I specify an endpoint and a date range and I'm supposed to retrieve a list of playlists. What I have now never comes back after the Start() call. Note: this is running in a WinForm. I am trying to better understand Tasks and don't just want to jump to awaits or a BackgroundWorker. I know I'm getting lost somewhere.
private void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
_getPlaylistsFunc = delegate()
{
var client = new PlaylistExportClient(baseUrl);
return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
};
var task = new Task<List<Playlist>>(_getPlaylistsFunc);
task.ContinueWith((t) => DisplayPlaylists(t.Result));
task.Start();
}
private void DisplayPlaylists(List<Playlist> playlists)
{
_queueDataGridView.DataSource = playlists;
}
UPDATE
I made these changes but now the application seems to hang the UI thread.
private void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
var token = Task.Factory.CancellationToken;
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
var client = new PlaylistExportClient(baseUrl);
_queueDataGridView.DataSource = client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
},token,TaskCreationOptions.None,context);
}

I recommend you use the Task-based Asynchronous Pattern. It's quite straightforward:
private async void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
var playlists = await Task.Run(() =>
{
var client = new PlaylistExportClient(baseUrl);
return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
});
_queueDataGridView.DataSource = playlists;
}
Note that this will block a threadpool thread; if you can modify the library to have a GetPlaylistsByDateRangeAsync method, you can make this more efficient.
Edit: If you're stuck on .NET 4.0, you can install Microsoft.Bcl.Async to get full async/await capabilities. If - for some inexplicable reason - you still can't use async/await, then you can do it like this:
private void GoButtonClick(object sender, EventArgs e)
{
string baseUrl = "http://someserver/api";
var startDateTime = this._startDateTimePicker.Value;
var endDateTime = this._endDateTimePicker.Value;
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(() =>
{
var client = new PlaylistExportClient(baseUrl);
return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList();
}).ContinueWith(t =>
{
_queueDataGridView.DataSource = t.Result;
}, context);
}
However, note that your error handling is more complex with this approach.

It looks like you're assigning to a property of a UI control in a background thread. That's usually bad news. WPF usually throws an exception when you do that, not sure about WinForms.
Capture the data in the background thread, but switch back to the main UI thread before assigning it to a UI control. Try posting the data to the UI thread using something like
var uiSync = SynchronizationContext.Current;
Task.Factory.StartNew(() =>
{
var client = new PlaylistExportClient(baseUrl);
var list = client.GetPlaylistsByDateRange(...).ToList();
uiSync.Post(() => _queueDataGridView.DataSource = list, null);
},token,TaskCreationOptions.None,context);

Related

How to run the UI Grafik and reader function without letting the UI get stopped until function ends? [duplicate]

I have a button that after I click it send a lot of data in a remote database with a loop, but during this operation whole wpf UI is freezing. My goal is to make the loader work while it is processing everything with the database.
My button code:
private void btn_Start_Click(object sender, RoutedEventArgs e)
{
pb_loader.IsIndeterminate = true; //<- it has to start to make animation
IEmailService emailService = new EmailService();
IUserQueryService emailQueryService = new UserQueryService();
var idIniziale = int.Parse(txtIdIniziale.Text);
var idFinale = int.Parse(txtIdFinale.Text);
var n = idFinale - idIniziale;
string mail = "";
for(int i=0; i<=n; i++)
{
mail = txtMail.Text + idIniziale + "#mail.local";
var exist = emailQueryService.CheckUserExist(mail); //<- db operation method
if (exist == false)
{
var lastUniqueId = emailQueryService.GetLastUniqueId();//<- db operation method
lastUniqueId = lastUniqueId + 1;
var idUtente = emailService.SalvaUtente(mail, lastUniqueId); //<- db operation method
emailService.AssegnaReferente(idUtente, txtMail.Text);//<- db operation method
emailService.AssegnaRuoli(idUtente); //<- db operation method
}
idIniziale++;
}
pb_loader.IsIndeterminate = false; //<- it has to end animation of loading
}
One straighforward approach for running a background operation in an event handler is to declare the event handler async and run and await a Task:
private async void btn_Start_Click(object sender, RoutedEventArgs e)
{
// prevent click while operation in progress
btn_Start.IsEnabled = false;
pb_loader.IsIndeterminate = true;
// access UI elements before running the Task
var mail = txtMail.Text + idIniziale + "#mail.local";
...
await Task.Run(() =>
{
// perform background operation
// use local variables "mail" etc. here
});
pb_loader.IsIndeterminate = false;
btn_Start.IsEnabled = true;
}

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

C# uwp client server show components

I have a problem with my server uwp side.
I try do unhide some components like this but my app hangs on :
private async void StreamSocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using(var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => this.txtBlk_Events.Text = request);
if (request.Length > 0)
{
btnSend.Visibility = Visibility.Visible;
}
}
It is possible to do that or not ?
Thanks for your answer
Visibility is a UI property and must be set on the UI thread. In your case it will run on the same thread the ConnectionReceived event handler is run on (thanks to async/await) which is likely not the UI thread. You should instead set the Visibility within the Dispatcher.RunAsync call to make sure it runs on UI thread.
private async void StreamSocketListener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using(var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
var setVisibility = request.Length > 0;
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.txtBlk_Events.Text = request;
if ( setVisibility )
{
btnSend.Visibility = Visibility.Visible;
}
});
}

Unresponsive UI while using Async & Await

I am trying to use async and await in my coding while I transfer big amounts of data from my WCF service to my WPF client application. Now it does take about 2-3 minutes to load the data, but now I struggle with my UI that's unresponsive for that total time. It's not very user-friendly. Is there a way to get my my UI responsive while the data loads with the coding that I currently have?
public pgSysproStock()
{
InitializeComponent();
SysproStock.WindowState = WindowState.Normal;
this.UpdateStockAsync();
}
private async void UpdateStockAsync()
{
dgSysproStock.IsEnabled = false;
using (TruckServiceClient TSC = new TruckServiceClient())
{
var allStock = await TSC.GetSysproStockAsync();
dgSysproStock.ItemsSource = allStock.Select(item =>
new AllStock
{
Id = item.Id,
StockCode = item.StockCode,
Description = item.Description,
ConvFactAltUom = item.ConvFactAltUom,
ConvMulDiv = item.ConvMulDiv,
ConvFactOthUom = item.ConvFactOthUom,
MulDiv = item.MulDiv,
Mass = item.Mass,
Updated_Supplier = item.Updated_Supplier,
CycleCount = item.CycleCount,
ProductClass = item.ProductClass.ToString(),
UnitCost = item.UnitCost,
Discount = item.Discount,
Warehouse = item.Warehouse,
MinimumStock = item.MinimumStock,
MaximumStock = item.MaximumStock,
StockForNow = item.StockForNow,
CoilWidth = item.CoilWidth,
SheetCoilLength = item.SheetCoilLength,
MaterialThickness = item.MaterialThickness
}).ToArray();
dgSysproStock.IsEnabled = true;
}
}
Thank you for any advice! :D
It seems that your operation is initiating operation incorrectly.
I suggest you create handler On Load or some other event for your window, mark it async and do the call from there. Should be simple as this:
private async void Form1_Load(object sender, EventArgs e)
{
dgSysproStock.IsEnabled = false;
using (TruckServiceClient TSC = new TruckServiceClient())
{
var allStock = await TSC.GetSysproStockAsync();
dgSysproStock.ItemsSource = allStock.Select(item =>
new AllStock
{
...
}).ToArray();
dgSysproStock.IsEnabled = true;
}
}
Note that I'm using Form.Load event but you may use different.

Cancel Parallel.ForEach or use async await

I have this event:
private void TextBoxSearchText_TextChanged(object sender, TextChangedEventArgs e)
{
searchText();
}
and I want to cancel this parallel method and start a new one when textbox text changes and also want my textbox be responsive to my new text typing, which is lock until results come to listbox.
List<TextList> oSelected;
private void searchText()
{
string strSearchText = TextBoxSearchText.Text;
oSelected = new List<TextList>();
Parallel.ForEach(oTextList, item =>
{
Match myMatch = Regex.Match(item.EnglishText.ToString(), "\\b" + strSearchText.ToString().ToLower() + #"\w*", RegexOptions.IgnoreCase);
if (!myMatch.Success)
{
return;
}
oSelected.Add(new TextList
{
Id = item.Id,
EnglishText = item.EnglishText
});
});
ListBoxAllTexts.ItemsSource = oSelected;
}
Is it possible to use async and awiat to accomplish the job?
Which one is better for searching a text in almost 1 million line of text?
I read alot about async and await but I couldn't understand how to use it in my work.
Thank you
Since your work is CPU-bound, you should use parallel code to do the actual search. However, you can wrap the parallel work in an async/await mindset by using Task.Run:
private async void TextBoxSearchText_TextChanged(object sender, TextChangedEventArgs e)
{
ListBoxAllTexts.ItemsSource = await Task.Run(() => searchText(TextBoxSearchText.Text));
}
This will keep your UI responsive.
To do cancellation, use a CancellationTokenSource. On a side note, you can't update a List<T> from a parallel loop like you're currently trying to do because List<T> is not threadsafe. In this scenario, I recommend you use PLINQ instead:
private CancellationTokenSource _cts;
private async void TextBoxSearchText_TextChanged(object sender, TextChangedEventArgs e)
{
if (_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
var strSearchText = TextBoxSearchText.Text;
ListBoxAllTexts.ItemsSource = await Task.Run(
() => searchText(strSearchText, _cts.Token));
}
private List<TextList> searchText(string strSearchText, CancellationToken token)
{
try
{
return oTextList.AsParallel().WithCancellation(token)
.Where(item => Regex.IsMatch(item.EnglishText.ToString(), "\\b" + strSearchText.ToLower() + #"\w*", RegexOptions.IgnoreCase))
.Select(item => new TextList
{
Id = item.Id,
EnglishText = item.EnglishText
})
.ToList();
}
catch (OperationCanceledException)
{
return null;
}
}
Also, consider throttling the user input by only starting the search after a delay. Rx is the best way to do that.

Categories