How to use Cancellation Token properly in this code? - c#

I have a GUI application where data is continously polled by MyMethod and when the Disconnect button is clicked, inside MyMethod the OperationCanceledException
closes the port. Here my_cancelationTokenSource is declared:
public partial class MainWindow : Window
{
private CancellationTokenSource my_cancelationTokenSource;
//...
}
And here MyMethod continously polls data:
private async Task MyMethod(ISerialComms port, byte[] test_telegram)
{
byte[] sent;
my_cancelationTokenSource = new CancellationTokenSource();
try
{
do
{
await Task.Delay(POLL_PERIOD, my_cancelationTokenSource.Token);
if (myFlag == false && sent != null && myPort.IsOpen == true && test_port == false)
{
byte[] received = await telegram.SendReceive(sent, myPort, port, my_cancelationTokenSource.Token);
MyProgressMethod(received, sent);
}
}
while (true);
}
catch (OperationCanceledException)
{
try
{
my_cancelationTokenSource.Dispose();
my_cancelationTokenSource = null;
if (myPort.IsOpen)
{
myPort.Close();
}
}
catch { }
}
}
Here is the disconnect button:
private void Button_Disconnect_Click(object sender, RoutedEventArgs e)
{
my_cancelationTokenSource?.Cancel();
}
And below I have another async method TestParameters() run in series inside another button:
private async void Test_Click(object sender, RoutedEventArgs e)
{
try
{
if (myPort.IsOpen)
{
myFlag = true;
byte[] set_parameter = myObject.CreatePulseParameters((double)NumericUpDown1.Value, (double)NumericUpDown2.Value);
int delay_ms = 20;
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms * 2);
await TestParameters(set_parameter);
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms * 4);
await TestParameters(set_parameter);
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms);
await TestParameters(set_parameter);
await Task.Delay(delay_ms * 3);
await TestParameters(set_parameter);
myFlag = false;
}
}
catch (System.InvalidOperationException)
{
myPort.Close();
}
}
My problem is, when I click Disconnect the port is closed but TestParameters() keeps running. How can this issue be solved?
(One solution was to put if(myPort.IsOpen) before each TestParameters(), but somehow I thought there misght be a more reilable better way)
Wht I need: TestParameters() should stop running as well when the Disconnect button is clicked just like MyMetho().
Edit:
Two other Tasks:
private async Task TestParameters(byte[] set_parameter)
{
if (myPort.IsOpen)
{
int times_try = 5;
int retry = 0;
myFlag = true;
await Task.Delay(500);
do
{
bool validate = await Set_Parameters(set_parameter);
await Task.Delay(500);
if (validate)
break;
if (!validate)
retry++;
}
while (retry < times_try);
}
myFlag = false;
}
And the other one:
private async Task<bool> Set_Parameters(byte[] send_set_parameter_telegram)
{
bool validate = false;
byte[] validate_parameters = pulse.validate_parameters;
if (myPort.IsOpen )
{
await Task.Delay(100);
byte[] receive_error_telegram = await telegram.SendReceive(validate_parameters, myPort, port);
await Task.Delay(100);
byte[] receive_validated_telegram = await telegram.SendReceive(validate_parameters, myPort, port);
}
return validate;
}

Related

UI only partly responsive while running tasks and updating ui

i have the following ui -
For each line connection to crm should be tested. This is done in separate thread.
The test status of the connection to crm system is then updated in last column.
The problem is that the ui is only partly reponsive during threads run and updating of the ui, i.e.
i would like to click through the lines whilst updating.
Here is my code:
private async void btnTestAllConnections_Click(object sender, EventArgs e)
{
await TestConnectionsAsync();
}
private async Task TestConnectionsAsync()
{
try
{
int idxConn = columnLookup[ColumnIndex.Connection].Index;
if (lvInitParameters.Items.Count == 0)
return;
ManagedConnection connection = null;
btnTestAllConnections.Visible = false;
btnTestConnection.Visible = false;
panel2.Enabled = false;
panel3.Enabled = false;
tableLayoutPanel1.Enabled = false;
btnCancelTest.Visible = true;
List<Task> tasks = new List<Task>();
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
foreach (ListViewItem lvi in lvInitParameters.Items)
{
InitParamProxy currentProfile = (InitParamProxy)lvi.Tag;
lvi.SubItems[idxConn].Text = "Testing...";
Task<bool> result =null;
try
{
result = Task.Run(
() =>
{
try
{
connection = currentProfile.ManagedConnection;
return connection?.ConnectionSuccess ?? false;
}
catch (Exception ex)
{
// crm exception
return false;
}
}, token);
if (token.IsCancellationRequested)
{
Console.WriteLine("\nCancellation requested in continuation...\n");
token.ThrowIfCancellationRequested();
}
ListViewItem testItem =
items.Where(si => ((InitParamProxy)lvi.Tag).ProfileKey.Equals(((InitParamProxy)si.Tag).ProfileKey)).SingleOrDefault();
lvi.SubItems[idxConn].Text = (result.Result) ? "Success" : "Fail";
if (testItem != null)
testItem.SubItems[idxConn].Text = (result.Result) ? "Success" : "Fail";
}
catch
{
ListViewItem testItem =
items.Where(si => ((InitParamProxy)lvi.Tag).ProfileKey.Equals(((InitParamProxy)si.Tag).ProfileKey)).SingleOrDefault();
lvi.SubItems[idxConn].Text = "Canceled";
if (testItem != null)
testItem.SubItems[idxConn].Text = "Canceled";
}
tasks.Add(result);
}
Task.WaitAll(tasks.ToArray());
btnTestAllConnections.Visible = true;
btnTestConnection.Visible = true;
panel2.Enabled = true;
panel3.Enabled = true;
tableLayoutPanel1.Enabled = true;
btnCancelTest.Visible = false;
}
catch (Exception)
{
}
}
In the end of your method you have
Task.WaitAll(tasks.ToArray());
This will block until all tasks are done. You should instead use WhenAll
await Task.WhenAll(tasks.ToArray());
You are also using result.Result in several places, and this also blocks. This should be replaced by awaiting the task, i.e. await result

CancellationToken in Web API not working correctly

I have Web Api which gets CancellationToken from users, the method inside it (DoWork) also get CancellationToken:
[HttpPost]
public async Task<long> GetInfo(CancellationToken cancellationToken)
{
long result = 0;
bool notDone = true;
Task<long> t = Task.Run(async () =>
{
if (cancellationToken.IsCancellationRequested)
cancellationToken.ThrowIfCancellationRequested();
while (notDone && !cancellationToken.IsCancellationRequested)
{
result = await DoWork(cancellationToken);
notDone = false;
}
return result;
}, cancellationToken);
try
{
return await t;
}
catch (AggregateException e)
{
Debug.WriteLine("Exception messages:");
foreach (var ie in e.InnerExceptions)
Debug.WriteLine(" {0}: {1}", ie.GetType().Name, ie.Message);
Debug.WriteLine("\nTask status: {0}", t.Status);
throw;
}
catch (Exception ex)
{
throw;
}
}
private Task<long> DoWork(CancellationToken token)
{
long result = 0;
bool notDone = true;
Task<long> task = Task.Run(() =>
{
if (token.IsCancellationRequested)
token.ThrowIfCancellationRequested();
while (notDone && !token.IsCancellationRequested)
{
Thread.Sleep(8000);
result = 2;
notDone = false;
}
return result;
}, token);
return task;
}
I expect when the user cancels the request it aborts the DoWork method and not continue the function, but after sending an Exception, when "Thread.Sleep" complete, the DoWork method continue.
the user can call the API service like this method "cc" as you can see it cancel after 5 seconds and in the DoWork method "Thread.Sleep" is 9 seconds. the user gets an Exception but the method still running.
private async Task<bool> cc()
{
UriBuilder builder = new UriBuilder("http://localhost:12458/api/Test/GetInfo");
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpClient client = new HttpClient();
System.Threading.CancellationTokenSource s = new System.Threading.CancellationTokenSource();
s.CancelAfter(5000);
try
{
var result = client.PostAsJsonAsync<model1>(builder.ToString(), new model1 { }, s.Token).Result;
string tmp = result.Content.ReadAsStringAsync().Result;
long ApiResult = JsonConvert.DeserializeObject<long>(tmp);
}
catch (TaskCanceledException ex)
{
}
catch (OperationCanceledException ex)
{
}
catch (Exception ex)
{
}
finally
{
s.Dispose();
}
return false;
}
When use Thread.Sleep(8000) actually hold the main thread for 8 seconds and It can't check the cancellation token. you should use Task.Delay with cancellation token like this:
while (notDone && !token.IsCancellationRequested)
{
await Task.Delay(8000, token);
result = 2;
notDone = false;
}
Task.Delay check the cancellation token itself.
Ok, in your code you should process CancellationToken.IsCancellationRequested too. It's not kinda of magic, you should do this work.
public void DoWork(CancellationToken ctsToken)
{
ctsToken.ThrowIfCancellationRequested();
DoSomething();
ctsToken.ThrowIfCancellationRequested();
DoSomethingElse();
// end so on with checking CancellationToken before every part of work
}
And your Task should look like this
Task<long> t = Task.Run(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
result = await DoWork(cancellationToken);
notDone = false;
cancellationToken.ThrowIfCancellationRequested();
return result;
}, cancellationToken);
In my case it was because of fiddler. When I closed the fiddler app, it started working like a charm.

Set a WPF Mahapps Progress Dialog

I'm trying to replace my ProgressBar to a Progress Dialog using Mahapps.
So I started writing this:
private void btnClick(object sender, RoutedEventArgs e)
{
ConfRelais();
}
public async void ConfRelais()
{
var controller = await this.ShowProgressAsync("hey", "hoy");
controller.Maximum = 128;
while (flag == 0)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok") { controller.SetMessage("Done Process");
flag = 1; }
else { controller.SetProgress(Int32.Parse(data)); }
}
await controller.CloseAsync();
}
But the progress dialog only displays when it's over.. As I'm still a beginner in c# maybe I'm missing some importants points to setup that kind of function.
You should execute the loop on a background thread:
public async void ConfRelais()
{
var controller = await this.ShowProgressAsync("hey", "hoy");
controller.Maximum = 128;
await Task.Run(() =>
{
while (flag == 0)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok")
{
controller.SetMessage("Done Process");
flag = 1;
}
else { controller.SetProgress(Int32.Parse(data)); }
}
});
await controller.CloseAsync();
}
A single thread cannot both update the UI and execute your loop simultaneously.
You also don't really need a flag. You could just break out of the loop when you receive "ok":
while (true)
{
string data = RelayBoard_Port.ReadTo("\r\n");
if (data == "ok")
{
controller.SetMessage("Done Process");
break;
}
else { controller.SetProgress(Int32.Parse(data)); }
}

UI hangs when multiple httpclient or httpresponse sent

trying to get page source from list of urls. but it hangs the UI. i would prefer httpclient so i can use single instance. please let me know what can i do so it wont hang the ui during the process.
async Task AccessTheWebAsync(CancellationToken ct)
{
HttpClient client = new HttpClient();
List<string> urlToSearch = UrlToSearchList();
IEnumerable<Task<string>> downloadTasksQuery =from url in urlToSearch select ProcessURL2(url, client, ct);
List<Task<string>> downloadTasks = downloadTasksQuery.ToList();
while (downloadTasks.Count > 0)
{
Task<string> firstFinishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(firstFinishedTask);
string length = await firstFinishedTask;
resultsTextBox.Text += $"\r\nLength of the download: {length}";
}
}
private List<string> UrlToSearchList()
{
var urlToSearchList = new List<string>();
foreach (var item in lbURLToSearch.Items)
{
urlToSearchList.Add(item.ToString());
}
return urlToSearchList;
}
async Task<string> ProcessURL2(string url, HttpClient client, CancellationToken ct)
{
HttpResponseMessage response = await client.GetAsync(url, ct);
var contents = await response.Content.ReadAsStringAsync();
return contents;
}
private void CancelButton_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
private async void StartButton_Click(object sender, EventArgs e)
{
resultsTextBox.Clear();
cts = new CancellationTokenSource();
try
{
await AccessTheWebAsync(cts.Token);
resultsTextBox.Text += "\r\nDownloads complete.";
}
catch (OperationCanceledException)
{
resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
}
catch (Exception)
{
resultsTextBox.Text += "\r\nDownloads failed.\r\n";
}
cts = null;
}
i also tried various async methods. still same result, it hangs. below is one of the async method i tried.
private async void TsbtnStartURLScraper_Click(object sender, EventArgs e)
{
for (int i = 0; i < lbURLToSearch.Items.Count; i++)
{
txtScrapeURL.Text = lbURLToSearch.Items[i].ToString();
string urlAddress = lbURLToSearch.Items[i].ToString();
var returnedresult = await DownloadStringV1(urlAddress);
resultsTextBox.Text += returnedresult;
}
}
public async Task<String> DownloadStringV1(String url)
{
// good code
var request = await reusableHTTPClient.GetAsync(url);
var download = await request.Content.ReadAsStringAsync();
return download;
}
NOTE: what i am trying to do is, loop through a list of url and get page source from them. but when i am doing this it is freezing the form.

How to cancel await SerialPort.BaseStream.ReadAsync?

My c# WinForms application reading asynchronously from serial port. It works, but when the port closes, an StackOverflowException appears. The Call Stack is overflowed with line await ReadAsync(serialPort) on the end of ReadAsync. My guess is the line await ReadAsync still run even though I close SeriaPort, keepReading flag and checking serialPort.IsOpen. Hence the question, how to cancel await ReadAsync, is it possible? Appreciate some help here because I'm beginner.
public async Task ReadAsync(SerialPort serialPort)
{
if (serialPort.IsOpen && keepReading)
{
try
{
byte[] buffer = new byte[1];
await serialPort.BaseStream.ReadAsync(buffer, 0, 1);
content += serialPort.Encoding.GetString(buffer);
//do sth with content
buffer = null;
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
if (keepReading && serialPort.IsOpen)
await ReadAsync(serialPort);
}
}
private async void Button_Click(object sender, EventArgs e)
{
if (serialPort.IsOpen)
{
keepReading = false;
serialPort.Dispose();
serialPort.Close();
}
else
{
Open();
keepReading = true;
if (serialPort.IsOpen)
{
serialPort.DiscardOutBuffer();
serialPort.DiscardInBuffer();
await ReadAsync(serialPort);
}
}
}
Try to wrap serialPort.BaseStream.ReadAsync(buffer, 0, 1); into another async method and use Task Cancellation to cancel this method before you close the serial port.
Cancel an Async Task or a List of Tasks (C#)

Categories