I want to implement a class which runs a background task, looping and updating the state of the class. Once the class falls out of scope, running the background task has no value, so I would like to exit the loop and allow the task to complete and release the thread.
public class ValueProvider
{
private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
public string Value {get; private set;}
public IChangeToken ChangeToken => _reloadToken;
public void Load()
{
// Load the initial value.
Value = "Foo";
// Watch for changes to and reload the value.
WatchForChanges();
}
private void WatchForChanges() => Task.Run(WatchAsync);
private async Task WatchAsync()
{
var stillInScope = true;
while(stillInScope)
{
// Watch for new values.
Value = await LoadNewValueAsync();
// Signal a change.
OnReload();
}
}
private async Task<string> LoadNewValueAsync()
{
// Simulate an actual lookup which blocks until a new value is available.
await Task.Delay(TimeSpan.FromSeconds(30));
return "Bar";
}
private void OnReload()
{
var previousToken = Interlocked.Exchange(
ref _reloadToken,
new ConfigurationReloadToken());
previousToken.OnReload();
}
}
Since the created Task has a reference to the ValueProvider, the instance will never fall out of scope. The solution probably has to include accessing the ValueProvider from the background task via a WeakReference<T> to allow it to go out of scope, but I'm open to solutions.
How can I detect that the "foreground" object has fallen out of scope and stop the accompanied background task?
Related
The question may sound a bit abstract, here's an example of a non async piece of code that does some lazy loading and ensures that the lazy loading is only done once by the first thread:
public class LazyExample
{
private object ExpensiveResource = null;
private object ExpensiveResourceSyncRoot = new object();
public object GetExpensiveResource()
{
if (ExpensiveResource != null) // Checkpoint 1
return ExpensiveResource;
lock (ExpensiveResourceSyncRoot) // initially there will be a queue of threads waiting here that have already passed Checkpoint 1
{
if (ExpensiveResource != null) // prevent re-retrieval by all subsequent threads that passed Checkpoint 1
return ExpensiveResource;
// create the expensive resource but do not assign yet...
object expensiveResource = new object();
// initialize the expensive resource, for example:
// - Call an API...
// - Do some Database lookups...
Thread.Sleep(1);
// finally as last step before releasing the lock, assign the fully initialized expensive object
ExpensiveResource = expensiveResource;
}
return ExpensiveResource;
}
}
In our lib, the async virus has started to infest many calls. Since awaiting is not allowed directly inside a lock we now wrap the async stuff in a new method like so:
public class LazyExample
{
private object ExpensiveResource = null;
private object ExpensiveResourceSyncRoot = new object();
public async Task<object> GetExpensiveResourceAsync()
{
if (ExpensiveResource != null) // Checkpoint 1
return ExpensiveResource;
lock (ExpensiveResourceSyncRoot) // initially there will be a queue of threads waiting here that have already passed Checkpoint 1
{
if (ExpensiveResource != null) // prevent re-retrieval by all subsequent threads that passed Checkpoint 1
return ExpensiveResource;
// assign the fully initialized expensive object
ExpensiveResource = CreateAndInitializeExpensiveResourceAsync().Result;
}
return ExpensiveResource;
}
private async Task<object> CreateAndInitializeExpensiveResourceAsync()
{
object expensiveResource = new object();
// initialize the expensive resource, this time async:
await Task.Delay(1);
return expensiveResource;
}
}
This however feels like putting a zip-tie around a safety latch and defeating the cause.
In seemingly random cases we need to wrap a call in order to prevent deadlocks like so:
ExpensiveResource = Task.Run(CreateAndInitializeExpensiveResourceAsync).Result;
This will force the method to run in a different thread and cause the current thread to go idle until it joins (extra overhead for no good reason as far as I can tell).
So my question is: is it safe to offload async stuff to a separate method (a new stack frame if you will) inside a lock or are we indeed defeating a safety latch?
Synchronously waiting for your async operation to finish, putting a lock around that, and then punting that off to the ThreadPool to make it async again is a solution, but it's just about the worst option out there.
First, why not use an async lock? SemaphoreSlim is the classic:
public class LazyExample
{
private object ExpensiveResource = null;
private readonly SemaphoreSlim ExpensiveResourceSyncRoot = new(1, 1);
public async Task<object> GetExpensiveResourceAsync()
{
if (ExpensiveResource != null) // Checkpoint 1
return ExpensiveResource;
await ExpensiveResourceSyncRoot.WaitAsync();
try
{
if (ExpensiveResource != null) // prevent re-retrieval by all subsequent threads that passed Checkpoint 1
return ExpensiveResource;
// finally as last step before releasing the lock, assign the fully initialized expensive object
ExpensiveResource = await CreateAndInitializeExpensiveResourceAsync();
}
finally
{
ExpensiveResourceSyncRoot.Release();
}
return ExpensiveResource;
}
}
Secondly, there's a much better solution to this problem of lazy async initialisation. Instead of caching ExpensiveResource, cache a Task which represents the initialisation of ExpensiveResource. When a consumer awaits that Task, either they'll find that it's already complete, in which case the resource is available immediately, or they'll have to wait, in which case the resource is still being created, and by awaiting the Task they'll be notified when it is available.
public class LazyExample
{
private Task<object> ExpensiveResource = null;
private readonly object ExpensiveResourceSyncRoot = new();
// Note this is not async, and does not contain awaits
public Task<object> GetExpensiveResourceAsync()
{
if (ExpensiveResource != null)
return ExpensiveResource;
lock (ExpensiveResourceSyncRoot)
{
if (ExpensiveResource != null)
return ExpensiveResource;
// Note that we don't await this.
// ExpensiveResource holds the Task which represents the creation of the resource
ExpensiveResource = CreateAndInitializeExpensiveResourceAsync();
}
return ExpensiveResource;
}
}
We can further simplify with a Lazy<T>:
public class LazyExample
{
private readonly Lazy<Task<object>> ExpensiveResource;
public LazyExample()
{
ExpensiveResource = new(CreateAndInitializeExpensiveResourceAsync);
}
public Task<object> GetExpensiveResourceAsync() => ExpensiveResource.Value;
}
async void Main()
{
T0.TT();
}
private class T0
{
[ThreadStatic] private static int test;
public static async void TT()
{
test = 4;
var continuation = new System.Threading.Tasks.TaskCompletionSource<int>(System.Threading.Tasks.TaskContinuationOptions.RunContinuationsAsynchronously);
var th = new Thread(() => { Thread.Sleep(500); Console.WriteLine(test); test = 3; continuation.TrySetResult(5); test = 7; });
th.Start();
Console.WriteLine(await continuation.Task);
Console.WriteLine(test);
}
}
Output:
0
5
3
So without the System.Threading.Tasks.TaskContinuationOptions.RunContinuationsAsynchronously this was written to demonstrate the rest of the async method runs on the thread created by new Thread(). However with System.Threading.Tasks.TaskContinuationOptions.RunContinuationsAsynchronously it still somehow finds that specific [ThreadStatic] value that is set in the newly created thread (thus can't be a TaskScheduler thread) and cleared as soon as TrySetResult returns.
What the hey? How is this happening?
You should be passing TaskCreationOptions.RunContinuationsAsynchronously, not TaskContinuationOptions.RunContinuationsAsynchronously.
Passing TaskContinuationOptions.RunContinuationsAsynchronously will call the overload that takes an object parameter, treating it as a "state" object and not as a flag controlling the TCS behavior.
Suppose I have a List<Task>:
private readonly List<Task> _tasks = new List<Task>(new Task[9]);
I want create a Task for start the DoWorkAsync method available inside the class Foo, so what I did is the following:
_tasks[0] = new Task<Foo>(() => new Foo().DoWorkAsync());
so the Foo class contains DoWorkAsync method which have a design similar to this:
public async Task HeavyAsync()
{
while (true)
{
string newData = DateTime.Now.ToLongTimeString();
Console.WriteLine(newData);
await Task.Delay(200);
}
}
actually I can start the Task using _tasks[0].Start();.
This works, but the main problem is that I want access to the public property of Foo class and I can't, because when I type this: _tasks[0].
I'll get the following method:
I also tried using GetAwaiter() and await:
var foo = await _tasks[0];
but I'll get the following error:
Cannot assign void to an implicitly-typed local variable
How can I access to the Foo properties?
You have to change the interface of your method. A task is just a "function pointer" that is executed and then finished. If you need the object, that contains the executed function, then you have to save or return it somewhere.
I would do the following: change the async method to return an object like:
public async Task<Foo> HeavyAsync()
{
while (true)
{
string newData = DateTime.Now.ToLongTimeString();
Console.WriteLine(newData);
await Task.Delay(200);
}
return this;
}
then this statement
var foo = await _tasks[0];
should give you with foo a reference to your Foo-object.
UPDATE:
Or you give your class Foo the following property and methods:
class Foo
{
private Task runningTask {get;set;}
public void StartTask()
{
runningTask = Task.Start( () => ....);
}
public async Task WaitTask()
{
await runningTask;
}
public bool IsRunning => runningTask != null && runningTask.Status......
And instead of holding a list of Task in your calling method, you might hold the list of Foo instances.
How can I change a Observable source while keeping the subscription intact? Yea, this is easy to accomplish with a Subject but best practises says not to use a subject.
Here an example:
Worker.cs
public class Worker
{
public IObservable<Worker> IsWorking {get;}
public string Name {get;}
public Worker(string name)
{
Name = name;
IsWorking = Observable.Interval(TimeSpan.FromSeconds(1))
.Select(_ => this);
}
}
This class exposes an Observable which fires every second.
WorkerState.cs
public class WorkerState
{
public Worker CurrentWorker
{
set => WorkerIsBusy = value.IsWorking;
}
public IObservable<Worker> WorkerIsBusy { get; private set; }
}
This class is supposed to hold a worker unit, which can be changed on the fly but the Subscription should be automatically attached to the current worker. (This is would be the place for a Subject)
And here the test:
Program.cs
public class Program
{
public async Task Run()
{
var state = new WorkerState();
state.CurrentWorker = new Worker("A");
state.WorkerIsBusy.Subscribe(worker =>
Console.WriteLine($"Worker {worker.Name}"));
await Task.Delay(3000);
Console.WriteLine("Should change now to B...");
state.CurrentWorker = new Worker("B");
Console.Read();
}
}
But my ouput is the following:
Worker A
Worker A
Should change now to B...
Worker A
Worker A
Worker A
...
instead of switching to worker B.
The approach you should almost always use for re-subscribing is to get the query to resubscribe for you by using the .Switch() operator.
Something like this:
var switcher = new Subject<IObservable<int>>();
var subscription = switcher.Switch().Subscribe(x => Console.WriteLine(x));
switcher.OnNext(Observable.Return(42));
switcher.OnNext(Observable.Range(0, 3));
That produces:
42
0
1
2
No re-subscription required.
For your code you need to change WorkerState to this:
public class WorkerState
{
private Worker currentWorker = null;
private Subject<Worker> currentWorkerSubject = new Subject<Worker>();
public Worker CurrentWorker
{
set
{
currentWorker = value;
currentWorkerSubject.OnNext(value);
}
}
public IObservable<Worker> WorkerIsBusy
{
get =>
currentWorkerSubject
.StartWith(currentWorker)
.Where(w => w != null)
.Select(w => w.IsWorking)
.Switch();
}
}
Then your code works (with the minor caveat that the await Task.Delay(3000); has a small race condition with the Observable.Interval(TimeSpan.FromSeconds(1)) inside Worker. Change the await to 3500 to avoid it.
The issue that you have is that you've subscribed to the previous value of WorkerIsBusy, and although you've changed the reference, you haven't changed the subscription, which still points at the previous value.
You would need to intercept (actually subscribe to) the worker.IsWorking in your WorkerState class, and unsubscribe / resubscribe when the property value changes.
Or you could use a Subject - I don't understand why you wouldn't?
Take the following classes as an example.
public class A
{
// ...
void Foo(S myStruct){...}
}
public class B
{
public A test;
// ...
void Bar()
{
S myStruct = new S();
test.Foo(myStruct);
}
}
Now, I want the method-call test.Foo(myStruct) to be an asynchronous call ('fire-and-forget'). The bar-method needs to return as soon as possible. Documentation around delegates, BeginInvoke, EndInvoke, the ThreadPool etc. isn't helping me find a solution.
Is this a valid solution?
// Is using the `EndInvoke` method as the callback delegate valid?
foo.BeginInvoke(myStruct, foo.EndInvoke, null);
You are not required to call EndInvoke; not calling it merely means:
You don't get the return value from the method.
Any exceptions thrown during the method execution will simply disappear.
It sounds like you want to 'fire-and-forget', so the easiest way to do this is to use an anonymous delegate, for example:
var del = new Action(foo.Bar);
del.BeginInvoke(iar =>
{
try
{
del.EndInvoke(iar);
}
catch (Exception ex)
{
// Log the message?
}
}, null);
This is what happens when you execute this code:
A new thread is allocated (put simply) for the delegate.
The thread is given the delegate del and the anonymous delegate (iar => ...).
The thread executes del.
When it is finished executing (or an exception occurs) the result or exception is stored and the anonymous delegate is executed.
Inside the anonymous delegate, when EndInvoke is called the result from the method is either returned, or the exception is thrown (if one occurred).
Note that the above example is very different from:
// This is pointless and is still, essentially, synchronous.
del.EndInvoke(del.BeginInvoke(null, null));
Edit: You should always call End*. I've never found a scenario where not calling it presents a problem, however that is an implementation detail and is relying on undocumented behavior.
Finally your solution would crash the process if an exception is thrown, you can simply pass null as the delegate if you don't care about the exception (del.BeginInvoke(myStruct, null, null);). So as a final example what you are looking for is probably:
public class A
{
// ...
void Foo(S myStruct){...}
void FooAsync(S myStruct)
{
var del = new Action<S>(Foo);
del.BeginInvoke(myStruct, SuppressException, del);
}
static void SuppressException(IAsyncResult ar)
{
try
{
((Action<S>)ar.AsyncState).EndInvoke(ar);
}
catch
{
// TODO: Log
}
}
}
I would say that your best option is to use the ThreadPool:
void bar()
{
ThreadPool.QueueUserWorkItem(o=>
{
S myStruct = new S();
test.foo(myStruct);
});
}
This will queue the snippet for execution in a separate thread. Now you also have to be careful about something else: if you have multiple threads accessing the same instance of A and that instance modifies a variable, then you must ensure that you do proper synchronization of the variable.
public class A
{
private double sum;
private volatile bool running;
private readonly object sync;
public A()
{
sum = 0.0;
running = true;
sync = new object();
}
public void foo(S myStruct)
{
// You need to synchronize the whole block because you can get a race
// condition (i.e. running can be set to false after you've checked
// the flag and then you would be adding the sum when you're not
// supposed to be).
lock(sync)
{
if(running)
{
sum+=myStruct.Value;
}
}
}
public void stop()
{
// you don't need to synchronize here since the flag is volatile
running = false;
}
}
You can use the Callback model explained # What is AsyncCallback?
That way your EndInvoke will not be in bar(), but in a separate callback method.
In the example, the EndRead (corresponding to EndInvoke is in the callback method called CompleteRead rather than the calling method TestCallbackAPM corresponding to bar)
This is an option:
ThreadPool.QueueUserWorkItem(bcl =>
{
var bcList = (List<BarcodeColumn>)bcl;
IAsyncResult iftAR = this.dataGridView1.BeginInvoke((MethodInvoker)delegate
{
int x = this.dataGridView1.Rows[0].Cells.Count - 1;
for (int i = 0; i < this.dataGridView1.Rows.Count - 1; i++)
{
try
{
string imgPath = bcList[i].GifPath;
Image bmpImage = Image.FromFile(imgPath);
this.dataGridView1.Rows[i].Cells[x].Value =bmpImage;
}
catch (Exception)
{
continue;
}
}
});
while (!iftAR.IsCompleted) { /* wait this*/ }
}, barcodeList);