Run method Safe cancellation? - c#

I want to call a function in a different thread than my GUI.
I used the below code to trigger the function:
private void button1_Click(object sender, EventArgs e)
{
tokensource = new CancellationTokenSource();
var token = tokensource.Token;
Task.Run(()=>foo() , token);
}
private void foo()
{
// Uses some resources
}
private void button2_Click(object sender, EventArgs e)
{
tokenSource.Cancel();
}
How can I safely close the occupied resources in foo() when the task is cancelled?

You need to pass the token to the function as well. The cancellation token passed to Task.Run won't abort an already running task, it will prevent a scheduled task from running.
Inside foo, you can check the token for cancellation and return, or throw an exception. You can use a using block to dispose resources safely. Eg:
private void foo(CancellationToken token)
{
using(var reader=new StreamReader(somePath)
{
string line;
// Read the line if no cancellation was requested
while (!token.IsCancellationRequested && (line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
This code reads a line only if cancellation wasn't requested and returns quietly otherwise
You can also throw an OperationCancelledException by calling CancellationToken.ThrowIfCancellationRequested
private void foo(CancellationToken token)
{
using(var reader=new StreamReader(somePath)
{
string line;
// Read the line if no cancellation was requested
while ((line = sr.ReadLine()) != null)
{
token.ThrowIfCancellationRequested();
Console.WriteLine(line);
}
}
}
This will throw an exception that will be raised in the calling code when the task's result is retrieved, eg when using await Task.Run(..) or Task.Run(..).Wait()

Your Method should handle the CancellationToken like that:
public static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
Console.WriteLine("Press CTRL+C to cancel important work");
Console.CancelKeyPress += (sender, eventArgs) => {
eventArgs.Cancel = true;
tokenSource.Cancel();
};
var task = Task.Run(() => foo(tokenSource.Token));
task.Wait();
WaitFor(action: "exit");
}
private static void foo(CancellationToken token)
{
const int Times = 10;
for (var x = 0; x < Times && token.IsCancellationRequested == false; ++x) {
Console.WriteLine("Important work");
Task
.Delay(200)
.Wait();
}
Console.WriteLine($"Free resources: {token.IsCancellationRequested}");
}
public static void WaitFor(ConsoleKey consoleKey = ConsoleKey.Escape, string action = "continue")
{
Console.Write($"Press {consoleKey} to {action} ...");
var consoleKeyInfo = default(ConsoleKeyInfo);
do {
consoleKeyInfo = Console.ReadKey(true);
}
while (Equals(consoleKeyInfo.Key, consoleKey) == false);
Console.WriteLine();
}
BR
incureforce

The code you're spinning up in a task should be responsible of taking into account cancellation. And the cancellation token you pass into the "Task.Run" method will be used only to cancel non-started tasks.

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.

Alternatives for Monitor (Wait, PluseAll) in Async Tasks in C#

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

Correct way of starting a forever-running loop with Async

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

Enhance performance for threading

I have an app with a start button that calls a long running time function. In order
to add a Stop button I've added a thread for this function to avoid the UI freezes and be able to stop the processing in anytime.
The code without threading in average takes 12 minutes to complete the processing, but with threading in the way I have below
takes 4 times more. Below is shown the code for the start button where is called the function "LongRunningFunction" . The function
needs a string argument to work "LongRunningFunction(Somestring)".
I've tested with Task.Run and Task.Factory.StartNew but it happens the same with both methods.
Is there an alternative way to set a thread for my case that doesn't affect too much the performance?
public partial class Form1 : Form
{
CancellationTokenSource cts = new CancellationTokenSource(); // Create the token source.
public Form1()
{
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
cts = new CancellationTokenSource();
Task.Run(()=> LongRunningFunction(Somestring, cts.Token), cts.Token);
//Task.Factory.StartNew(() => LongRunningFunction(Somestring, cts.Token), cts.Token, TaskCreationOptions.None, TaskScheduler.Default);
}
private void Stop_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
cts = null;
MessageBox.Show("Processing cancelled");
}
}
public void LongRunningFunction(string String, CancellationToken token)
{
//Long running processing
//...
MessageBox.Show("Processing finished");
}
}
Update:
The only what I changed is the way I declare the function and added an if statement inside the while loop
that is inside the function. Is like below:
The CancelationToken was added in order to be able to stop the processing when Stop button is pressed.
Without thread I declare the function like this:
public void LongRunningFunction(string String)
{
while (condition)
{
//My code within While loop
}
MessageBox.Show("Processing finished");
}
and with Thread I define the function like this:
public void LongRunningFunction(string String, CancellationToken token)
{
while (condition)
{
if (token.IsCancellationRequested)
{
break;
}
//My code within While loop
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
Update2:
Inside LongRunningFunction() is called another function that prints the lines. Is like below.
public void LongRunningFunction(string fileName, CancellationToken token)
{
StreamWriter writer = new StreamWriter(#outputfile, true, Encoding.UTF8, 4096);
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
List<byte> buffer = new List<byte>();
List<string> buffer1 = new List<string>();
SoapHexBinary hex = new SoapHexBinary();
while (chunk.Length > 0)
{
if (token.IsCancellationRequested) // ### For Cancel Thread ###
{
break;
} // ### For Cancel Thread ###
chunk = reader.ReadBytes(1024);
foreach (byte data in chunk)
{
if (somecondition)
{
buffer.Add(data);
}
else if (other condition)
{
buffer.Add(data);
PrintFunction(buffer, hex, outputfile, writer); // Print Line
}
else if (some other condition)
{
buffer.Add(data);
}
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
if (writer != null)
{
writer.Dispose();
writer.Close();
}
}
private void PrintFunction(List<byte> buffer, SoapHexBinary hex, string outputfile, StreamWriter writer)
{
if (buffer.Count > 0)
{
if (buffer.Count >= lowlimit)
{
hex.Value = buffer.ToArray();
string Register = hex.ToString();
Regex pattern1 = new Regex(#"some pattern");
if (pattern1.IsMatch(Register))
{
Match l1 = Regex.Match(Register, #"somepattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
writer.Write("{0}|{1}|{2}", Convert.ToInt32(l1.Groups[1].ToString(), 16), l1.Groups[2].Value, l1.Groups[3].Value);
Match l2 = Regex.Match(Register, #"otherpattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
if (l2.Success)
{
foreach (Match m in Regex.Matches(l2.Groups[2].ToString(), pattern2, RegexOptions.IgnoreCase | RegexOptions.Compiled))
{
//Some foreach code
}
foreach (Match x in Regex.Matches(var, #"pattern"))
{
//come code
}
writer.WriteLine("," + String.Join(",", var1));
}
else
{
writer.WriteLine();
}
}
}
}
buffer.Clear();
}
Update3:
Hi bebosh,
I still have doubts how to apply in my function, the way you define the delegate in your example function.
My function looks like this:
public void LongRunningFunction(string fileName)
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
}
It could be something like this or how?:
private void LongRunningFunction(string fileName)
{
MethodInvoker action = delegate
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
};
}
Bebosh's answer was good enough. To increase the performance further you can set the ThreadPriority of the "thread" by setting the ".Priority = ThreadPriority.AboveNormal" right after setting the "thread.IsBackground = true;".
Could you try this code:
bool Stop = false;
Thread thread;
private void StartButton_Click(object sender, EventArgs e)
{
string FileName = #"...\a.bin";
thread = new Thread(new ThreadStart(() => DoLongProcess(FileName)));
thread.IsBackground = true;
thread.Start();
}
private void StopButton_Click(object sender, EventArgs e)
{
Stop = true;
}
private void DoLongProcess(string file)
{
using (BinaryReader reader = new BinaryReader(File.Open(file, FileMode.Open)))
{
int pos = 0;
int length = (int)reader.BaseStream.Length;
while (pos < length)
{
if (Stop)
thread.Abort();
// using Invoke if you want cross UI objects
this.Invoke((MethodInvoker)delegate
{
label1.Text = pos.ToString();
});
pos += sizeof(int);
}
}
}
use interrupting the thread
Thread thread;
public MainWindow()
{
InitializeComponent();
}
private async void StartButtonClick(object sender, RoutedEventArgs e)
{
thread = new Thread(ExecuteLong);
var task = Task.Run(() =>
thread.Start());
await task;
}
private void ExecuteLong()
{
try
{
// long task
}
catch (ThreadInterruptedException e)
{
MessageBox.Show("cancelled!");
return;
}
MessageBox.Show("finished");
}
private void CancelButtonClick(object sender, RoutedEventArgs e)
{
this.thread.Interrupt();
}

Monitor Async Task with Timeout and cancellation

I All,
I have to monitor an async task, which has to be cancellable and do not perform more than specific Time To Live.
I already knew about the following code.
CancellationTokenSource l_cts = new CancellationTokenSource(timemillis);
which will doing the cancellation ( as far i monitor the token in my async method).
However, this NOT gave me any information about WHY he has been cancelled, Timeout or user cancellation? furthermore, the timeout event is delayed while i did not catch the cancellation with
Token.ThrowIfCancellationRequested();
In order to solve these issues, I wrote the timeout process as follow.
static async Task TestAsync(int processDelaySeconds, int cancelDelaySeconds, int timeoutDelaySeconds )
{
CancellationTokenSource l_cts = new CancellationTokenSource();
// the process to monitor
Task l_process = new Task((state) =>
{
Console.WriteLine("Process BEGIN");
// dummy loop
for (int l_i = 0; l_i != processDelaySeconds; l_i++)
{
Thread.Sleep(1000);
l_cts.Token.ThrowIfCancellationRequested();
}
Console.WriteLine("Process END");
}, null, l_cts.Token);
// register timeout
RegisteredWaitHandle l_rwh = ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle,
(state, timedOut) =>
{
if (timedOut)
{
l_cts.Cancel();
Console.WriteLine("Timed out");
}
else
{
Console.WriteLine("Cancel Signaled");
}
},
null, (int)TimeSpan.FromSeconds(timeoutDelaySeconds).TotalMilliseconds, true);
// cancel task
if (cancelDelaySeconds > 0)
{
Task l_cancel = new Task(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(cancelDelaySeconds));
l_cts.Cancel();
});
l_cancel.Start();
}
try
{
l_process.Start();
await l_process;
}
catch (OperationCanceledException)
{
Console.WriteLine("Task Cancelled");
}
finally
{
// be sure to unregister the wait handle to cancel the timeout
if (l_process.Status != TaskStatus.Canceled) l_rwh.Unregister(l_cts.Token.WaitHandle);
}
Console.WriteLine("Task Status is : {0}", l_process.Status);
}
static async void Tests()
{
Console.WriteLine("NORMAL PROCESS");
Console.WriteLine("--------------");
await TestAsync(2, 10, 10);
Console.WriteLine();
Console.WriteLine("CANCEL");
Console.WriteLine("------");
await TestAsync(5, 2, 10);
Console.WriteLine();
Console.WriteLine("TIMEOUT");
Console.WriteLine("-------");
await TestAsync(10, 15, 2);
}
Then My question is :
Is there any drawbacks or traps behind the scene ?
A better and more efficient way ??
ps- Goal is performance, not shorter code.
If you need to distinguish between the user's and time-out cancellation, you can use CreateLinkedTokenSource:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
internal class Program
{
// worker
private static void DoWork(CancellationToken token)
{
for (int i = 0; i < 1000; i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(100); // do the work item
}
token.ThrowIfCancellationRequested();
}
// test
private static void Main()
{
var userCt = new CancellationTokenSource();
var combinedCt = CancellationTokenSource.CreateLinkedTokenSource(
userCt.Token);
combinedCt.CancelAfter(3000); // cancel in 3 seconds
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
userCt.Cancel();
};
var task = Task.Run(
() => DoWork(combinedCt.Token),
combinedCt.Token);
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.InnerException.Message);
if (task.IsCanceled)
{
if (userCt.Token.IsCancellationRequested)
Console.WriteLine("Cancelled by user");
else if (combinedCt.Token.IsCancellationRequested)
Console.WriteLine("Cancelled by time-out");
else
Console.WriteLine("Cancelled by neither user nor time-out");
}
}
}
}
}
As to your original code, you really did not need ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle, ...), there's CancellationToken.Register for that, which returns an IDisposable ready for use with using.
In order to know if your Task has been cancelled or timed out, you can use the Task.WaitAny overload which takes a TimeSpan:
// Index will return -1 if timeout has occured, otherwise will print the index of the completed task
var cnclToken = new CancellationTokenSource().Token
var yourTask = Task.Run(() => { /* Do stuff */ }, cnclToken);
var index = Task.WhenAny(new[] { yourTask }, TimeSpan.FromSeconds(1));
http://msdn.microsoft.com/en-us/library/dd235645(v=vs.110).aspx

Categories