I'm trying to use Tokens to cancel Task started by Task.Run. I took pattern from microsoft site: https://msdn.microsoft.com/pl-pl/library/hh160373(v=vs.110).aspx
This is my code:
public static class Sender
{
public static async Task sendData(NetworkController nc) {
await Task.Run(() => {
IPEndPoint endPoint = new IPEndPoint(nc.serverIp, nc.dataPort);
byte[] end = Encoding.ASCII.GetBytes("end");
while (true) {
if (Painting.pointsQueue.Count > 0 && !nc.paintingSenderToken.IsCancellationRequested) {
byte[] sendbuf = Encoding.ASCII.GetBytes(Painting.color.ToString());
nc.socket.SendTo(sendbuf, endPoint);
do {
sendbuf = Painting.pointsQueue.Take();
nc.socket.SendTo(sendbuf, endPoint);
} while (sendbuf != end && !nc.paintingSenderToken.IsCancellationRequested);
}
else if (nc.paintingSenderToken.IsCancellationRequested) {
nc.paintingSenderToken.ThrowIfCancellationRequested();
return;
}
}
}, nc.paintingSenderToken);
}
}
And here I start this task:
public void stopController() {
try {
paintingSenderTokenSource.Cancel();
senderTask.Wait();
} catch(AggregateException e) {
string message = "";
foreach (var ie in e.InnerExceptions)
message += ie.GetType().Name + ": " + ie.Message + "\n";
MessageBox.Show(message, "Przerwano wysylanie");
}
finally {
paintingSenderTokenSource.Dispose();
byte[] message = Encoding.ASCII.GetBytes("disconnect");
IPEndPoint endPoint = new IPEndPoint(serverIp, serverPort);
socket.SendTo(message, endPoint);
socket.Close();
mw.setStatus("disconnected");
}
}
public async void initialize() {
Task t = Reciver.waitForRespond(this);
sendMessage("connect");
mw.setStatus("connecting");
if (await Task.WhenAny(t, Task.Delay(5000)) == t) {
mw.setStatus("connected");
Painting.pointsQueue = new System.Collections.Concurrent.BlockingCollection<byte[]>();
senderTask = Sender.sendData(this);
}
else {
mw.setStatus("failed");
}
}
}
In initialize() method I'm waiting for the response from the server and if I get it I start new thread in this sendData() method. It is in static class to make code cleaner. If I want to stop this thread I call stopController() method. In microsoft site we can read:
The CancellationToken.ThrowIfCancellationRequested method throws an OperationCanceledException exception that is handled in a catch block when the calling thread calls the Task.Wait method.
But my program breaks onnc.paintingSenderToken.ThrowIfCancellationRequested(); which is in 'sendData()' method and the error says that OperationCanceledException was not handled. I started program from microsoft site and it works perfectly. I think I'm doing everything like they did but unfortunately it doesnt't work like it should.
You may have "Enable Just My Code" enabled.
To find the settings go to:
Tools => Options => Debugging => General => Enable Just My Code
If this checkbox is checked could you un-check it and then run your application again.
Related
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.
I implemented Task synchronization using Monitor in C#.
However, I have read Monitor should not be used in asynchronous operation.
In the below code, how do I implement Monitor methods Wait and PulseAll with a construct that works with Task (asynchronous operations).
I have read that SemaphoreSlim.WaitAsync and Release methods can help.
But how do they fit in the below sample where multiple tasks need to wait on a lock object, and releasing the lock wakes up all waiting tasks ?
private bool m_condition = false;
private readonly Object m_lock = new Object();
private async Task<bool> SyncInteralWithPoolingAsync(
SyncDatabase db,
List<EntryUpdateInfo> updateList)
{
List<Task> activeTasks = new List<Task>();
int addedTasks = 0;
int removedTasks = 0;
foreach (EntryUpdateInfo entryUpdateInfo in updateList)
{
Monitor.Enter(m_lock);
//If 5 tasks are waiting in ProcessEntryAsync method
if(m_count >= 5)
{
//Do some batch processing to obtian values to set for adapterEntry.AdapterEntryId in ProcessEntryAsync
//.......
//.......
m_condition = true;
Monitor.PulseAll(m_lock); // Wakes all waiters AFTER lock is released
}
Monitor.Exit(m_lock);
removedTasks += activeTasks.RemoveAll(t => t.IsCompleted);
Task processingTask = Task.Run(
async () =>
{
await this.ProcessEntryAsync(
entryUpdateInfo,
db)
.ContinueWith(this.ProcessEntryCompleteAsync)
.ConfigureAwait(false);
});
activeTasks.Add(processingTask);
addedTasks++;
}
}
private async Task<bool> ProcessEntryAsync(SyncDatabase db, EntryUpdateInfo entryUpdateInfo)
{
SyncEntryAdapterData adapterEntry =
updateInfo.Entry.AdapterEntries.FirstOrDefault(e => e.AdapterId == this.Config.Id);
if (adapterEntry == null)
{
adapterEntry = new SyncEntryAdapterData()
{
SyncEntry = updateInfo.Entry,
AdapterId = this.Config.Id
};
updateInfo.Entry.AdapterEntries.Add(adapterEntry);
}
m_condition = false;
Monitor.Enter(m_lock);
while (!m_condition)
{
m_count++;
Monitor.Wait(m_lock);
}
m_count--;
adapterEntry.AdapterEntryId = .... //Set Value obtained form batch processing
Monitor.Exit(m_lock);
}
private void ProcessEntryCompleteAsync(Task<bool> task, object context)
{
EntryProcessingContext ctx = (EntryProcessingContext)context;
try
{
string message;
if (task.IsCanceled)
{
Logger.Warning("Processing was cancelled");
message = "The change was cancelled during processing";
}
else if (task.Exception != null)
{
Exception ex = task.Exception;
Logger.Warning("Processing failed with {0}: {1}", ex.GetType().FullName, ex.Message);
message = "An error occurred while synchronzing the changed.";
}
else
{
message = "The change was successfully synchronized";
if (task.Result)
{
//Processing
//...
//...
}
}
}
catch (Exception e)
{
Logger.Info(
"Caught an exception while completing entry processing. " + e);
}
finally
{
}
}
Thanks
I've an existing code I wrote some time ago, that works but I dislike the fact that the thread I start remains in loop.
This piece of code is a consumer on an IBMMQ code, waiting for messages to be processed.The problem I've is that with the following code
private Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
ConnectToAccessQueue();
Logger.Debug($"Accessed to the queue {queueName}");
Logger.DebugFormat("Repeating timer started, checking frequency: {checkingFrequency}",
checkingFrequency);
while (!cancellationToken.IsCancellationRequested)
{
Logger.Trace( () => "Listening on queues for new messages");
// isChecking = true;
var mqMsg = new MQMessage();
var mqGetMsgOpts = new MQGetMessageOptions
{ WaitInterval = (int)checkingFrequency.TotalMilliseconds };
// 15 second limit for waiting
mqGetMsgOpts.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING |
MQC.MQCNO_RECONNECT_Q_MGR | MQC.MQOO_INPUT_AS_Q_DEF;
try
{
mqQueue.Get(mqMsg, mqGetMsgOpts);
if (string.Compare(mqMsg.Format, MQC.MQFMT_STRING, StringComparison.Ordinal) == 0)
{
var text = mqMsg.ReadString(mqMsg.MessageLength);
Logger.Debug($"Message received : [{text}]");
Message message = new Message { Content = text };
foreach (var observer in observers)
observer.OnNext(message);
}
else
{
Logger.Warn("Non-text message");
}
}
catch (MQException ex)
{
if (ex.Message == MQC.MQRC_NO_MSG_AVAILABLE.ToString())
{
Logger.Trace("No messages available");
//nothing to do, emtpy queue
}
else if (ex.Message == MQC.MQRC_CONNECTION_BROKEN.ToString())
{
Logger.ErrorException("MQ Exception, trying to recconect", ex);
throw new ReconnectException();
}
}
Thread.Sleep(100);
}
},cancellationToken);
}
//Calling method
try
{
string queueManagerName = configuration.GetValue<string>("IBMMQ:QUEUE_MANAGER_NAME");
// var queueManager = new MQQueueManager(queueManagerName,dictionary2);
QueueMonitor monitor = new QueueMonitor(configuration, "IMPORTER_RECEIVER_TEST");
//_subscription = monitor.Subscribe(receiver);
await monitor.StartAsync(cts.Token).ConfigureAwait(false);
}
catch (Exception e)
{
log.Error(e, "Error creating the queue monitor or it's subscription");
}
finally
{
WaitForCancel(cts);
}
The call to await monitor.StartAsync(cts.Token).ConfigureAwait(false); remains pending.
How should I modify my code, so that the call returns and in background the task continue to loop?
Thanks in advance
Here is how you can simplify your code by replacing Thread.Sleep with Task.Delay:
private async Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
while (true)
{
// Process mqQueue here
await Task.Delay(100, cancellationToken);
}
}
Task.Delay has the advantage that accepts a CancellationToken, so in case of cancellation the loop will exit immediately. This could be important if the pooling of the MQ was lazier (for example every 5 seconds).
private static Task _runningTask;
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
_runningTask = ExecuteQueuePolling(cts.Token);
WaitForCancel(cts);
}
private static void WaitForCancel(CancellationTokenSource cts)
{
var spinner = new SpinWait();
while (!cts.IsCancellationRequested
&& _runningTask.Status == TaskStatus.Running) spinner.SpinOnce();
}
private static Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
var t = new Task(() =>
{
while (!cancellationToken.IsCancellationRequested)
; // your code
if (cancellationToken.IsCancellationRequested)
throw new OperationCanceledException();
}, cancellationToken, TaskCreationOptions.LongRunning);
t.Start();
return t;
}
The WPF code below hangs forever when network connection is lost for 3 or more minutes. When connection is restored it neither throws nor continues downloading nor timeouts. If network connection is lost for a shorter period say half a minute, it throws after connection is restored. How can i make it more robust to survive network outage?
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Windows;
namespace WebClientAsync
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
NetworkChange.NetworkAvailabilityChanged +=
(sender, e) => Dispatcher.Invoke(delegate()
{
this.Title = "Network is " + (e.IsAvailable ? " available" : "down");
});
}
const string SRC = "http://ovh.net/files/10Mio.dat";
const string TARGET = #"d:\stuff\10Mio.dat";
private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
btnDownload.IsEnabled = false;
btnDownload.Content = "Downloading " + SRC;
try {
using (var wcl = new WebClient())
{
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
await wcl.DownloadFileTaskAsync(new Uri(SRC), TARGET);
btnDownload.Content = "Downloaded";
}
}
catch (Exception ex)
{
btnDownload.Content = ex.Message + Environment.NewLine
+ ((ex.InnerException != null) ? ex.InnerException.Message : String.Empty);
}
btnDownload.IsEnabled = true;
}
}
}
UPDATE
Current solution is based on restarting Timer in DownloadProgressChangedEventHandler, so the timer fires only if no DownloadProgressChanged events occur within the timeout. Looks like an ugly hack, still looking for a better solution.
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace WebClientAsync
{
public partial class MainWindow : Window
{
const string SRC = "http://ovh.net/files/10Mio.dat";
const string TARGET = #"d:\stuff\10Mio.dat";
// Time needed to restore network connection
const int TIMEOUT = 30 * 1000;
public MainWindow()
{
InitializeComponent();
}
private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
btnDownload.IsEnabled = false;
btnDownload.Content = "Downloading " + SRC;
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Timer timer = new Timer((o) =>
{
// Force async cancellation
cts.Cancel();
}
, null //state
, TIMEOUT
, Timeout.Infinite // once
);
DownloadProgressChangedEventHandler handler = (sa, ea) =>
{
// Restart timer
if (ea.BytesReceived < ea.TotalBytesToReceive && timer != null)
{
timer.Change(TIMEOUT, Timeout.Infinite);
}
};
btnDownload.Content = await DownloadFileTA(token, handler);
// Note ProgressCallback will fire once again after awaited.
timer.Dispose();
btnDownload.IsEnabled = true;
}
private async Task<string> DownloadFileTA(CancellationToken token, DownloadProgressChangedEventHandler handler)
{
string res = null;
WebClient wcl = new WebClient();
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
wcl.DownloadProgressChanged += handler;
try
{
using (token.Register(() => wcl.CancelAsync()))
{
await wcl.DownloadFileTaskAsync(new Uri(SRC), TARGET);
}
res = "Downloaded";
}
catch (Exception ex)
{
res = ex.Message + Environment.NewLine
+ ((ex.InnerException != null) ? ex.InnerException.Message : String.Empty);
}
wcl.Dispose();
return res;
}
}
}
You need to implement proper timeout for that download. But you don't need to use timer, just use Task.Delay and Task.WaitAny. For example:
static async Task DownloadFile(string url, string output, TimeSpan timeout) {
using (var wcl = new WebClient())
{
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
var download = wcl.DownloadFileTaskAsync(url, output);
// await two tasks - download and delay, whichever completes first
await Task.WhenAny(Task.Delay(timeout), download);
var exception = download.Exception; // need to observe exception, if any
bool cancelled = !download.IsCompleted && exception == null;
// download is not completed yet, nor it is failed - cancel
if (cancelled) {
wcl.CancelAsync();
}
if (cancelled || exception != null) {
// delete partially downloaded file if any (note - need to do with retry, might not work with a first try, because CancelAsync is not immediate)
int fails = 0;
while (true) {
try {
File.Delete(output);
break;
}
catch {
fails++;
if (fails >= 10)
break;
await Task.Delay(1000);
}
}
}
if (exception != null) {
throw new Exception("Failed to download file", exception);
}
if (cancelled) {
throw new Exception($"Failed to download file (timeout reached: {timeout})");
}
}
}
Usage:
const string SRC = "http://ovh.net/files/10Mio.dat";
const string TARGET = #"d:\stuff\10Mio.dat";
// Time needed to restore network connection
TimeSpam TIMEOUT = TimeSpan.FromSeconds(30);
DownloadFile(SRC,TARGET, TIMEOUT); // might want to await this to handle exceptions
Update in response to comment. If you want timeout based on received data, not on whole operation time, it's also possible with Task.Delay. For example:
static async Task DownloadFile(string url, string output, TimeSpan timeout)
{
using (var wcl = new WebClient())
{
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
DateTime? lastReceived = null;
wcl.DownloadProgressChanged += (o, e) =>
{
lastReceived = DateTime.Now;
};
var download = wcl.DownloadFileTaskAsync(url, output);
// await two tasks - download and delay, whichever completes first
// do that until download fails, completes, or timeout expires
while (lastReceived == null || DateTime.Now - lastReceived < timeout) {
await Task.WhenAny(Task.Delay(1000), download); // you can replace 1 second with more reasonable value
if (download.IsCompleted || download.IsCanceled || download.Exception != null)
break;
}
var exception = download.Exception; // need to observe exception, if any
bool cancelled = !download.IsCompleted && exception == null;
// download is not completed yet, nor it is failed - cancel
if (cancelled)
{
wcl.CancelAsync();
}
if (cancelled || exception != null)
{
// delete partially downloaded file if any (note - need to do with retry, might not work with a first try, because CancelAsync is not immediate)
int fails = 0;
while (true)
{
try
{
File.Delete(output);
break;
}
catch
{
fails++;
if (fails >= 10)
break;
await Task.Delay(1000);
}
}
}
if (exception != null)
{
throw new Exception("Failed to download file", exception);
}
if (cancelled)
{
throw new Exception($"Failed to download file (timeout reached: {timeout})");
}
}
}
Personally, if I were to make a robust download solution, I would add a Network connection monitor because that's what we are actually waiting for. For simplicity, something like this will be enough.
online = true;
NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
_isNetworkOnline = NetworkInterface.GetIsNetworkAvailable();
void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
online = e.IsAvailable;
}
Then you can actually check for network availability and wait as appropriate before you attempt to download or progress... I will definitely accept that a simple ping solution seems to work better than this at times based on experience.
Depending on the size of what you're downloading, monitoring the network speed may also help so you can decide how to chunk in case of choppy connections. Take a look at this project for ideas.
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();.