I'm really new to threading multitasking/multithreading, but I'm working on a project where I think I need it. The user will be editing a fairly complex diagram, and I want the program to check for validity of the diagram. The validity check is non-trivial (polynomial time, though, not NP - seconds, not minutes or years, but I don't want to hold the user up for a few seconds after every change) so I would like the program to check for validity in the background and highlight inconsistencies when it finds them. When the user makes certain changes to the diagram (changes the structure, not just the labels on elements), the validation will have to throw away what it was doing and start again. I'm assuming the user will eventually take a break to think/go for a pee/go for a coffee/chat to that rather cute person two cubicles along, but in case they don't, I have to let the validation run to completion in some circumstances (before a save or a printout, for example). Broad-brush, what are the features of C# I'll need to learn, and how do I structure that?
Broad Brush. Here we go.
Q: "What are the features of C# I'll need to learn?"
A: You can get by nicely with a basic toolkit consisting (roughly speaking) of:
System.Threading.Tasks.Task
System.Threading.CancellationTokenSource
System.Threading.SemaphoreSlim
Q: "I don't want to hold the user up for a few seconds after every change"
A: OK, so we will never-ever block the UI thread. Fire off a Task to run a background validation routine that checks every now and then to see if it's been cancelled.
CancellationTokenSource _cts = null;
SemaphoreSlim ssBusy = new SemaphoreSlim(2);
private void ExecValidityCheck()
{
ssBusy.Wait();
Task.Run(() =>
{
try
{
_cts = new CancellationTokenSource();
LongRunningValidation(_cts.Token);
}
finally
{
ssBusy.Release();
}
})
.GetAwaiter()
.OnCompleted(CheckForRestart);
}
We'll call CheckForRestart using GetAwaiter().OnCompleted(). This just means that without blocking we'll be notified as a callback when the thread finishes for one of three reasons:
Cancelled
Cancelled, but with an intent to start the validation over from the beginning.
Ran validation to completion
By calling CheckForRestart we determine whether to start it over again or not.
void CheckForRestart()
{
BeginInvoke((MethodInvoker)delegate
{
if (_restart)
{
_restart = false;
ExecValidityCheck();
}
else
{
buttonCancel.Enabled = false;
}
});
}
Rather that post the complete code here, I pushed a simple working example to our GitHub. You can browse it there or clone and run it. 20-second screen capture. When the RESTART button is clicked in the video, it's checking the CurrentCount property of the Semaphore. In a threadsafe way it determines whether the validation routine is already running or not.
I hope I've managed to give you a few ideas about where to start. Sure, the explanation I've given here has a few holes but feel free to address your critical concerns in the comments and I'll try to respond.
You probably need to learn about asynchronous programming with async/await, and about cooperative cancellation. The standard practice for communicating cancellation is by throwing an OperationCanceledException. Methods that are intended to be cancelable accept a CancellationToken as argument, and observe frequently the IsCancellationRequested method of the token. So here is the basic structure of a cancelable Validate method with a boolean result:
bool Validate(CancellationToken token)
{
for (int i = 0; i < 50; i++)
{
// Throw an OperationCanceledException if cancellation is requested
token.ThrowIfCancellationRequested();
Thread.Sleep(100); // Simulate some CPU-bound work
}
return true;
}
The "driver" of the CancellationToken is a class named CancellationTokenSource. In your case you'll have to create multiple instances of this class, one for every time that the diagram is changed. You must store them somewhere so that you can call later their Cancel method, so lets make two private fields inside the Form, one for the most recent CancellationTokenSource, and one for the most recent validation Task:
private Task<bool> _validateTask;
private CancellationTokenSource _validateCTS;
Finally you'll have to write the logic for the event handler of the Diagram_Changed event. It is probably not desirable to have multiple validation tasks running side by side, so it's a good idea to await for the completion of the previous task before launching a new one. It is important that awaiting a task doesn't block the UI. This introduces the complexity that multiple Diagram_Changed events, along with other unrelated events, can occur before the completion of the code inside the handler. Fortunately you can count on the single-threaded nature of the UI, and not have to worry about the thread-safety of accessing the _validateTask and _validateCTS fields by multiple asynchronous workflows. You do need to be aware though that after every await these fields may hold different values than before the await.
private async void Diagram_Changed(object sender, EventArgs e)
{
bool validationResult;
using (var cts = new CancellationTokenSource())
{
_validateCTS?.Cancel(); // Cancel the existing CancellationTokenSource
_validateCTS = cts; // Publish the new CancellationTokenSource
if (_validateTask != null)
{
// Await the completion of the previous task before spawning a new one
try { await _validateTask; }
catch { } // Ignore any exception
}
if (cts != _validateCTS) return; // Preempted (the event was fired again)
// Run the Validate method in a background thread
var task = Task.Run(() => Validate(cts.Token), cts.Token);
_validateTask = task; // Publish the new task
try
{
validationResult = await task; // Await the completion of the task
}
catch (OperationCanceledException)
{
return; // Preempted (the validation was canceled)
}
finally
{
// Cleanup before disposing the CancellationTokenSource
if (_validateTask == task) _validateTask = null;
if (_validateCTS == cts) _validateCTS = null;
}
}
// Do something here with the result of the validation
}
The Validate method should not include any UI manipulation code, because it will be running in a background thread. Any effects to the UI should occur after the completion of the method, through the returned result of the validation task.
Related
I have a slow proc gen function that runs when a user changes any parameters.
If a parameter is changed before it has completed, I want it to cancel the task and start a new one.
Currently I have it checking if a cancellation token is null, and if not requesting a cancellation before launching a new task.
public static async void Generate(myInputParams Input)
{
SectorData myData;
if (_tokenSource != null)
{
_tokenSource.Cancel();
_tokenSource.Dispose();
}
_tokenSource = new CancellationTokenSource();
var token = _tokenSource.Token;
myData = await Task.Run(() => SlowFunction(Input, token));
// do stuff with new data
}
This does work but it seems to me that it's possible for the new task to be run before the cleanup code and cancelation in the previous one have completed.
Is there a way I can guarantee the previous task is done before starting the new one?
EDIT:
I had forgotten to pass the token to the SlowFunction in this example. That wasn't the issue with my real one it just happened when I was renaming things to make it easier to read.
Also typos
This does work but it seems to me that it's possible for the new task to be run before the cleanup code and cancelation in the previous one have completed.
Is there a way I can guarantee the previous task is done before starting the new one?
Sure: save the task in a variable (just like you're currently doing with _tokenSource), and await it before starting the new task. You'll probably want to await it in a try/finally block and ignore errors, at least if they're OperationCanceledException kinds of errors.
I'm assuming that this is a single-threaded UI context, where Generate is an event handler called on the UI thread. Otherwise, your class-level variables will need to be protected by mutexes and the async void should be removed.
Yes, I know, there are tons of threads on this topic. I read a lot of them and used them often (more or less) successfully. Now I got an old DLL (programmed in .net 4.0) and that is using BackgroundWorkers to fire result events. Whatever I try to stop and wait for such a result seems to miss its mark. But maybe some of you have ideas that I haven't tried yet.
I register the answer event in thread 1 (according to Thread.CurrentThread.ManagedThreadId), call the method to connect and wait with a SemaphoreSlim for the answer. But in each and every constellation I get the answering event AFTER the timeout occurred. The event is on thread 3 and when I tried to raise the AwaitAsync() (also with Await) of the SemaphoreSlim on a special Thread.Run(() => ...); it was on thread id 8. But still thread number 3 always just comes AFTER the timeout.
private void ConnectDevice()
{
MobileDevice.DeviceConnected += new DeviceConnectedHandler(MobileDevice_Connected);
...
mSignal = new SemaphoreSlim(0, 1);
MobileDevice.Connect();
// Task.Run(() => MobileDevice.Connect());
int i = Thread.CurrentThread.ManagedThreadId;
var task = Task.Run(() => mSignal.WaitAsync(new TimeSpan(0, 0, cTimeout)).GetAwaiter().GetResult());
// Task<bool> task = Task.Run(async () => await WaitForSemaphore());
// var result = task.Wait();
IsConnected = task.Result;
// WaitForSemaphore();
...
}
private async Task<bool> WaitForSemaphore()
{
int j = Thread.CurrentThread.ManagedThreadId;
if (!await mSignal.WaitAsync(new TimeSpan(0, 0, 5)))
{
throw new MobileDeviceException("Device timed out");
}
return true;
}
//private void WaitForSemaphore()
//{
// if (!mSignal.Wait(new TimeSpan(0, 0, cTimeout)))
// {
// throw new MobileDeviceException("Device timed out");
// }
//}
private void MobileDevice_Connected(object sender, DeviceConnectedEventArgs e)
{
int k = Thread.CurrentThread.ManagedThreadId;
mSignal?.Release();
...
}
And yes, I know this is chaotic. But I wanted to show you, that I tried a lot already. I tried a lot more, but deleted also a mass of mistakes.
I begin to think, that, even if the answer comes on thread id 3, the listener to these events is still the main thread (id 1). And as long as that is blocked, the event doesn't gets fired.
Am I right? And how do I get around this? Register the event on a different way?
Oh, I nearly forgot: I am serving an Interface here and this will become a plugin for a complex application, so I cannot make the Connect-Method async and use the async/await-Pattern. I have to call the Connect of the device, block the main thread till the answer arrives and then release it, so the main part of the application can continue.
Anyone an idea of solving this?
Edit (the 1st): Ok, to sort some confusions out. This is a plugin that is called from a non-async method. I cannot change the calling method to an async one or else I would have to reprogram a few hundred thousand lines of code.
The call comes from the main program and looks like this:
firstDevice.Connect();
I COULD change that to something within reason, but I cannot use something like: await firstDevice.Connect(); or else I would have to change the main programs calls all to async. And this is simply out of question.
The connect method inside the plug-in I could change. At this moment it does nothing more than to call the ConnectDevice(), so I could test some things with async, SemaphoreSlims, and so on.
And as soon as I use an await inside an async method, the calling thread moves on. There would also have to be an await, but you cannot use await outside of async methods.
What seems strange to me is, that Thread.CurrentThread.ManagedThreadId says that both threads are thread 1. But when I step through they are clearly moving asynchronously.
Edit (the 2nd): I heard a clue. Maybe the problem here is the BackgroundWorker of the API. A colleague of mine once heard that the BackgroundWorker blocks the GUI-thread, when it is started on the GUI-thread. So the events of the API cannot get to me on thread 3 until the GUI-thread is released. So the solution would be to call the MobileDevice.Connect(); on a different thread. But it seems that the API will have to change. So we will discuss this internally. As soon as I have a solution I will update this a last time for anyone interested.
Edit (the 3rd): Ok, it seems nearly all of this solutions are working my problem was really with this goddamn BackgroundWorker. The API communicated with the mobile device on thread 1. And as soon as you block thread 1, there is also a block in the communication between API and device, so the answer of the API never comes...
But thanks anyway for you help. ;)
You may wrap the Connect call in a TaskCompletionSource:
public static class DeviceExtension
{
public static Task ConnectAsync(this Device device)
{
var tcs = new TaskCompletionSource<object>();
device.DeviceConnected += (s, e) => tcs.SetResult(null);
device.Connect();
return tcs.Task;
}
}
which you would call like
await MobileDevice.ConnectAsync();
or in a synchronous context like
MobileDevice.ConnectAsync().Wait();
Background:
I have a web application which kicks off long running (and stateless) tasks:
var task = Task.Run(() => await DoWork(foo))
task.Wait();
Because they are long running, I need to be able to cancel them from a separate web request.
For this, I would like to use a CancellationToken and just throw an exception as soon as the token is canceled. However, from what I've read, Task Cancellation is cooperative, meaning the code the task is running must explicitly check the token to see if a cancellation request has been made (for example CancellationToken.ThrowIfCancellation())
I would like to avoid checking CancellationToken.ThrowIfCancellation() all over the place, since the task is quite long and goes through many functions. I think I can accomplish what I want creating an explicit Thread, but I would really like to avoid manual thread management. That said...
Question:
Is it possible to automatically throw an exception in the task when it has been canceled, and if not, are there any good alternatives (patterns, etc.) to reduce polluting the code with CancellationToken.ThrowIfCancellation()?
I'd like to avoid something like this:
async Task<Bar> DoWork(Foo foo)
{
CancellationToken.ThrowIfCancellation()
await DoStuff1();
CancellationToken.ThrowIfCancellation()
await DoStuff2();
CancellationToken.ThrowIfCancellation()
await DoStuff3();
...
}
I feel that this question is sufficiently different from this one because I'm explicitly asking for a way to minimize calls to check the cancellation token, to which the accepted answer responds "Every now and then, inside the functions, call token.ThrowIfCancellationRequested()"
Is it possible to automatically throw an exception in the task when it has been canceled, and if not, are there any good alternatives (patterns, etc.) to reduce polluting the code with CancellationToken.ThrowIfCancellation()?
No, and no. All cancellation is cooperative. The best way to cancel code is to have the code respond to a cancellation request. This is the only good pattern.
I think I can accomplish what I want creating an explicit Thread
Not really.
At this point, the question is "how do I cancel uncancelable code?" And the answer to that depends on how stable you want your system to be:
Run the code in a separate Thread and Abort the thread when it is no longer necessary. This is the easiest to implement but the most dangerous in terms of application instability. To put it bluntly, if you ever call Abort anywhere in your app, you should regularly restart that app, in addition to standard practices like heartbeat/smoketest checks.
Run the code in a separate AppDomain and Unload that AppDomain when it is no longer necessary. This is harder to implement (you have to use remoting), and isn't an option in the Core world. And it turns out that AppDomains don't even protect the containing application like they were supposed to, so any apps using this technique also need to be regularly restarted.
Run the code in a separate Process and Kill that process when it is no longer necessary. This is the most complex to implement, since you'll also need to implement some form of inter-process communication. But it is the only reliable solution to cancel uncancelable code.
If you discard the unstable solutions (1) and (2), then the only remaining solution (3) is a ton of work - way, way more than making the code cancelable.
TL;DR: Just use the cancellation APIs the way they were designed to be used. That is the simplest and most effective solution.
If you actually just have a bunch of method calls you are calling one after the other, you can implement a method runner that runs them in sequence and checks in between for the cancellation.
Something like this:
public static void WorkUntilFinishedOrCancelled(CancellationToken token, params Action[] work)
{
foreach (var workItem in work)
{
token.ThrowIfCancellationRequested();
workItem();
}
}
You could use it like this:
async Task<Bar> DoWork(Foo foo)
{
WorkUntilFinishedOrCancelled([YourCancellationToken], DoStuff1, DoStuff2, DoStuff3, ...);
}
This would essentially do what you want.
If you are OK with the implications of Thread.Abort (disposables not disposed, locks not released, application state corrupted), then here is how you could implement non-cooperative cancellation by aborting the task's dedicated thread.
private static Task<TResult> RunAbortable<TResult>(Func<TResult> function,
CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<TResult>();
var thread = new Thread(() =>
{
try
{
TResult result;
using (cancellationToken.Register(Thread.CurrentThread.Abort))
{
result = function();
}
tcs.SetResult(result);
}
catch (ThreadAbortException)
{
tcs.TrySetCanceled();
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
});
thread.IsBackground = true;
thread.Start();
return tcs.Task;
}
Usage example:
var cts = new CancellationTokenSource();
var task = RunAbortable(() => DoWork(foo), cts.Token);
task.Wait();
I see a lot of options for canceling a long-running operation in C#, but each example seems to talk about cancelling parallel (multithreaded) operations or are overly-simple examples, or involve periodically polling for whether a request to cancel the operation was submitted. I don't think that will work here.
I have a method BuildZipFile() which, for now, takes no arguments, but I suspect might need a CancellationToken argument. Calling this method does the following. BuildZipFile() blocks; execution on the thread doesn't resume until it's done with its work.
Files are extracted and added to a zip file. This operation is so quick that I don't want it to be cancelable. If the user requests a cancel, it should ignore the request until the operation is complete, and then skip the rest of BuildZipFile() and return (or throw an exception; doesn't matter).
Files are processed using something called a "pipeline." This operation does take a long time and the user should be able to cancel it. To start this processing, BuildZipFile() calls a non-blocking method Start() on the pipeline. A pipeline raises Done when it's done with its work, so I use an AutoResetEvent to block the method until I hear that event, and then release the block.
Some more operations similar to item #1: quick-running operations that should not support cancelling.
Here's an overly-simplified implementation:
public void BuildZipFile()
{
// single-threaded operation that is quick and can't be canceled
DoQuickUncancelableThings();
// and now a long-running operation that the user SHOULD be able to cancel;
// it must be possible to interrupt the AutoResetEvent
var pipeline = GimmeAPipeline();
var reset = new AutoResetEvent(false);
// when the pipeline raises Done, stop blocking the method and resume execution
pipeline.Done += () => reset.Set();
// define the work to be done
ThreadPool.QueueUserWorkItem(state => pipeline.Start());
// call pipeline.Start() and block the thread until pipeline.Done is raised
reset.WaitOne();
// ...and more quick operations that can't be canceled
DoMoreQuickUncancelableThings();
}
Note that in reality, that middle block of code exists in another class which this one calls.
I can stop the pipeline in this method by calling pipeline.Stop() which will indirectly raise the Done event once the request to stop it was handled.
So, how can I modify BuildZipFile() to support user cancellation? My instinct is to add support for catching an OperationCanceledException, but that would allow those quick operations to cancel too, wouldn't it? And, I can't poll for a cancellation request unless I'm missing something because I'm waiting for that Done event from pipeline to be raised, and the last thing I want to do is poll using a timer to interrupt it.
I have no issues with modifying BuildZipFile() to become non-blocking, but the steps within it are very linear. Step #2 can't even start until step #1 is done; the process can't be made parallel. I cannot change how pipelines work; they must remain asynchronous and raise events when they're done.
I'm using .NET 4.5 in a Windows Forms application so I can use pretty much any framework feature I need.
I think you should use Tasks to do want you want.
Check this msdn article,it is very usefull
http://msdn.microsoft.com/en-us/library/dd537609(v=vs.110).aspx
Here is a full example in a console application
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static CancellationTokenSource tokenSource2;
static CancellationToken ct;
static void Main(string[] args)
{
tokenSource2 = new CancellationTokenSource();
ct = tokenSource2.Token;
Task myTask = new Task(BuildZipFile);
myTask.Start();
Console.WriteLine("Press enter to cancel");
Console.ReadLine();
tokenSource2.Cancel();
Console.ReadLine();
}
public static void BuildZipFile()
{
Task quick1 = new Task(DoQuickUncancelableThings);
quick1.ContinueWith(ant => DoLongRunnignThings(), ct).ContinueWith(ant => DoMoreQuickUncancelableThings());
quick1.Start();
}
private static void DoMoreQuickUncancelableThings()
{
Console.WriteLine("Q2");
}
private static void DoLongRunnignThings()
{
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
ct.ThrowIfCancellationRequested();
}
Console.WriteLine("Long ended");
}
private static void DoQuickUncancelableThings()
{
Console.WriteLine("Q1");
}
}
}
How can I implement a method with abort ability?
Imagine I have a grid, when data loads the grid starts calculating the totals and show them on another grid beneath itself. I want to start calculating totals right after loading data.
I tried using threads but a problem rose up:
When I click on Load button two rapid times, for the first time the thread starts working and the second time I get an error saying the thread is busy.
I need to implement some method that can be aborted and started again.
For this I would indeed use threads. Depending on your .NET framework you can start by setting up an IProgress object which can be used to provides UI updates from a background thread-pool thread. So firstly you could create
IProgress<object> progressIndicator =
new Progress<object>(ReportProgress);
where ReportProgress is some method
public void ReportProgress(object o) { /* Update UI components here on the Main UI thread */ }
Now, to start the work and provide cancellation support, you can do some thing like
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
Task task = Task.Factory.StartNew(() =>
{
// Do your work here and provide cancellation support.
for (int i = 0; i < someMaxValue; ++i)
{
// Update the UI if required.
progressIndicator.ReportProgress(someObject);
// No check for you cancellation condition...
if (source != null && someCancelCondition)
source.Cancel(); // This will throw a CancellationException().
}
}, token,
TaskCreationOptions.None,
TaskScheduler.Default)
.ContinueWith(ant =>
{
// Here you must handle you exceptions and trap your cancellation.
});
For more information see J. Albahari's Threading tutorial.
I hope this helps.
When load is clicked, create a CancellationTokenSource and store it. Pass in its CancellationToken to your worker thread, and check it regularly in your calculations so you can abort in a timely manner.
When Load is clicked again, you can abort the current thread by calling Cancel() on your TokenSource.